1
0

Compare commits

...

9 Commits

Author SHA1 Message Date
Hugh Nimmo-Smith 327555dddf Fix other test cases 2023-06-05 18:02:20 +01:00
Hugh Nimmo-Smith fba9000152 Changelog 2023-06-05 14:45:29 +01:00
Hugh Nimmo-Smith 2eb74c6bdb Enable login_via_existing_session by default 2023-06-05 14:42:31 +01:00
dependabot[bot] ca8906be2c Bump types-requests from 2.31.0.0 to 2.31.0.1 (#15715)
Bumps [types-requests](https://github.com/python/typeshed) from 2.31.0.0 to 2.31.0.1.
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-requests
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-05 10:39:34 +01:00
dependabot[bot] 2d97d5b1c3 Bump types-jsonschema from 4.17.0.7 to 4.17.0.8 (#15716)
Bumps [types-jsonschema](https://github.com/python/typeshed) from 4.17.0.7 to 4.17.0.8.
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-jsonschema
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-05 10:32:25 +01:00
dependabot[bot] 1a7aa81715 Bump sentry-sdk from 1.22.1 to 1.25.0 (#15714)
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 1.22.1 to 1.25.0.
- [Release notes](https://github.com/getsentry/sentry-python/releases)
- [Changelog](https://github.com/getsentry/sentry-python/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-python/compare/1.22.1...1.25.0)

---
updated-dependencies:
- dependency-name: sentry-sdk
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-05 10:32:16 +01:00
dependabot[bot] 5feabbdf06 Bump pyasn1 from 0.4.8 to 0.5.0 (#15713)
Bumps [pyasn1](https://github.com/pyasn1/pyasn1) from 0.4.8 to 0.5.0.
- [Release notes](https://github.com/pyasn1/pyasn1/releases)
- [Changelog](https://github.com/pyasn1/pyasn1/blob/main/CHANGES.rst)
- [Commits](https://github.com/pyasn1/pyasn1/compare/v0.4.8...v0.5.0)

---
updated-dependencies:
- dependency-name: pyasn1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-05 10:32:07 +01:00
dependabot[bot] 36a5bcae2c Bump library/redis from 6-bullseye to 7-bullseye in /docker (#15712)
Bumps library/redis from 6-bullseye to 7-bullseye.

---
updated-dependencies:
- dependency-name: library/redis
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-05 10:31:54 +01:00
dependabot[bot] 8ba530c0e3 Bump importlib-metadata from 6.1.0 to 6.6.0 (#15711)
Bumps [importlib-metadata](https://github.com/python/importlib_metadata) from 6.1.0 to 6.6.0.
- [Release notes](https://github.com/python/importlib_metadata/releases)
- [Changelog](https://github.com/python/importlib_metadata/blob/main/CHANGES.rst)
- [Commits](https://github.com/python/importlib_metadata/compare/v6.1.0...v6.6.0)

---
updated-dependencies:
- dependency-name: importlib-metadata
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-05 10:31:41 +01:00
13 changed files with 75 additions and 50 deletions
+1
View File
@@ -0,0 +1 @@
Enabled login_via_existing_session by default.
+1 -1
View File
@@ -21,7 +21,7 @@ FROM docker.io/library/debian:bullseye-slim AS deps_base
# which makes it much easier to copy (but we need to make sure we use an image
# based on the same debian version as the synapse image, to make sure we get
# the expected version of libc.
FROM docker.io/library/redis:6-bullseye AS redis_base
FROM docker.io/library/redis:7-bullseye AS redis_base
# now build the final image, based on the the regular Synapse docker image
FROM $FROM
@@ -2598,14 +2598,11 @@ ui_auth:
Matrix supports the ability of an existing session to mint a login token for
another client.
Synapse disables this by default as it has security ramifications -- a malicious
client could use the mechanism to spawn more than one session.
The duration of time the generated token is valid for can be configured with the
`token_timeout` sub-option.
User-interactive authentication is required when this is enabled unless the
`require_ui_auth` sub-option is set to `False`.
To protect against malicious clients abusing this capability, user-interactive authentication
is required unless the `require_ui_auth` sub-option is set to `False`.
Example configuration:
```yaml
Generated
+24 -23
View File
@@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand.
# This file is automatically @generated by Poetry and should not be changed by hand.
[[package]]
name = "alabaster"
@@ -867,14 +867,14 @@ files = [
[[package]]
name = "importlib-metadata"
version = "6.1.0"
version = "6.6.0"
description = "Read metadata from Python packages"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "importlib_metadata-6.1.0-py3-none-any.whl", hash = "sha256:ff80f3b5394912eb1b108fcfd444dc78b7f1f3e16b16188054bd01cb9cb86f09"},
{file = "importlib_metadata-6.1.0.tar.gz", hash = "sha256:43ce9281e097583d758c2c708c4376371261a02c34682491a8e98352365aad20"},
{file = "importlib_metadata-6.6.0-py3-none-any.whl", hash = "sha256:43dd286a2cd8995d5eaef7fee2066340423b818ed3fd70adf0bad5f1fac53fed"},
{file = "importlib_metadata-6.6.0.tar.gz", hash = "sha256:92501cdf9cc66ebd3e612f1b4f0c0765dfa42f0fa38ffb319b6bd84dd675d705"},
]
[package.dependencies]
@@ -1863,14 +1863,14 @@ psycopg2 = "*"
[[package]]
name = "pyasn1"
version = "0.4.8"
description = "ASN.1 types and codecs"
version = "0.5.0"
description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)"
category = "main"
optional = false
python-versions = "*"
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
files = [
{file = "pyasn1-0.4.8-py2.py3-none-any.whl", hash = "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d"},
{file = "pyasn1-0.4.8.tar.gz", hash = "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"},
{file = "pyasn1-0.5.0-py2.py3-none-any.whl", hash = "sha256:87a2121042a1ac9358cabcaf1d07680ff97ee6404333bacca15f76aa8ad01a57"},
{file = "pyasn1-0.5.0.tar.gz", hash = "sha256:97b7290ca68e62a832558ec3976f15cbf911bf5d7c7039d8b861c2a0ece69fde"},
]
[[package]]
@@ -2397,19 +2397,19 @@ doc = ["Sphinx", "sphinx-rtd-theme"]
[[package]]
name = "sentry-sdk"
version = "1.22.1"
version = "1.25.0"
description = "Python client for Sentry (https://sentry.io)"
category = "main"
optional = true
python-versions = "*"
files = [
{file = "sentry-sdk-1.22.1.tar.gz", hash = "sha256:052dff5069c6f0d836ee014323576824a9b40836fc003fb12489a1f19c60a3c9"},
{file = "sentry_sdk-1.22.1-py2.py3-none-any.whl", hash = "sha256:c6c6946f8c927adb00af1c5ab6921df38775b2199b9003816d5935a1310352d5"},
{file = "sentry-sdk-1.25.0.tar.gz", hash = "sha256:5be3296fc574fa8a4d9b213b4dcf8c8d0246c08f8bd78315c6286f386c37555a"},
{file = "sentry_sdk-1.25.0-py2.py3-none-any.whl", hash = "sha256:fe85cf5d0b3d0aa3480df689f9f6dc487de783defb0a95043368375dc893645e"},
]
[package.dependencies]
certifi = "*"
urllib3 = {version = ">=1.26.11,<2.0.0", markers = "python_version >= \"3.6\""}
urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""}
[package.extras]
aiohttp = ["aiohttp (>=3.5)"]
@@ -2421,10 +2421,11 @@ chalice = ["chalice (>=1.16.0)"]
django = ["django (>=1.8)"]
falcon = ["falcon (>=1.4)"]
fastapi = ["fastapi (>=0.79.0)"]
flask = ["blinker (>=1.1)", "flask (>=0.11)"]
flask = ["blinker (>=1.1)", "flask (>=0.11)", "markupsafe"]
grpcio = ["grpcio (>=1.21.1)"]
httpx = ["httpx (>=0.16.0)"]
huey = ["huey (>=2)"]
loguru = ["loguru (>=0.5)"]
opentelemetry = ["opentelemetry-distro (>=0.35b0)"]
pure-eval = ["asttokens", "executing", "pure-eval"]
pymongo = ["pymongo (>=3.1)"]
@@ -3037,14 +3038,14 @@ files = [
[[package]]
name = "types-jsonschema"
version = "4.17.0.7"
version = "4.17.0.8"
description = "Typing stubs for jsonschema"
category = "dev"
optional = false
python-versions = "*"
files = [
{file = "types-jsonschema-4.17.0.7.tar.gz", hash = "sha256:130e57c5f1ca755f95775d0822ad7a3907294e1461306af54baf804f317fd54c"},
{file = "types_jsonschema-4.17.0.7-py3-none-any.whl", hash = "sha256:e129b52be6df841d97a98f087631dd558f7812eb91ff7b733c3301bd2446271b"},
{file = "types-jsonschema-4.17.0.8.tar.gz", hash = "sha256:96a56990910f405e62de58862c0bbb3ac29ee6dba6d3d99aa0ba7f874cc547de"},
{file = "types_jsonschema-4.17.0.8-py3-none-any.whl", hash = "sha256:f5958eb7b53217dfb5125f0412aeaef226a8a9013eac95816c95b5b523f6796b"},
]
[[package]]
@@ -3124,14 +3125,14 @@ files = [
[[package]]
name = "types-requests"
version = "2.31.0.0"
version = "2.31.0.1"
description = "Typing stubs for requests"
category = "dev"
optional = false
python-versions = "*"
files = [
{file = "types-requests-2.31.0.0.tar.gz", hash = "sha256:c1c29d20ab8d84dff468d7febfe8e0cb0b4664543221b386605e14672b44ea25"},
{file = "types_requests-2.31.0.0-py3-none-any.whl", hash = "sha256:7c5cea7940f8e92ec560bbc468f65bf684aa3dcf0554a6f8c4710f5f708dc598"},
{file = "types-requests-2.31.0.1.tar.gz", hash = "sha256:3de667cffa123ce698591de0ad7db034a5317457a596eb0b4944e5a9d9e8d1ac"},
{file = "types_requests-2.31.0.1-py3-none-any.whl", hash = "sha256:afb06ef8f25ba83d59a1d424bd7a5a939082f94b94e90ab5e6116bd2559deaa3"},
]
[package.dependencies]
@@ -3424,18 +3425,18 @@ docs = ["Sphinx", "repoze.sphinx.autointerface"]
test = ["zope.i18nmessageid", "zope.testing", "zope.testrunner"]
[extras]
all = ["Pympler", "authlib", "hiredis", "jaeger-client", "lxml", "matrix-synapse-ldap3", "opentracing", "psycopg2", "psycopg2cffi", "psycopg2cffi-compat", "pyicu", "pysaml2", "sentry-sdk", "txredisapi"]
all = ["matrix-synapse-ldap3", "psycopg2", "psycopg2cffi", "psycopg2cffi-compat", "pysaml2", "authlib", "lxml", "sentry-sdk", "jaeger-client", "opentracing", "txredisapi", "hiredis", "Pympler", "pyicu"]
cache-memory = ["Pympler"]
jwt = ["authlib"]
matrix-synapse-ldap3 = ["matrix-synapse-ldap3"]
oidc = ["authlib"]
opentracing = ["jaeger-client", "opentracing"]
postgres = ["psycopg2", "psycopg2cffi", "psycopg2cffi-compat"]
redis = ["hiredis", "txredisapi"]
redis = ["txredisapi", "hiredis"]
saml2 = ["pysaml2"]
sentry = ["sentry-sdk"]
systemd = ["systemd-python"]
test = ["idna", "parameterized"]
test = ["parameterized", "idna"]
url-preview = ["lxml"]
user-search = ["pyicu"]
+1 -1
View File
@@ -63,7 +63,7 @@ class AuthConfig(Config):
# Logging in with an existing session.
login_via_existing = config.get("login_via_existing_session", {})
self.login_via_existing_enabled = login_via_existing.get("enabled", False)
self.login_via_existing_enabled = login_via_existing.get("enabled", True)
self.login_via_existing_require_ui_auth = login_via_existing.get(
"require_ui_auth", True
)
+3
View File
@@ -61,6 +61,9 @@ class MSC3861OAuthDelegation(TestCase):
**default_config("test"),
"public_baseurl": BASE_URL,
"enable_registration": False,
"login_via_existing_session": {
"enabled": False,
},
"experimental_features": {
"msc3861": {
"enabled": True,
+3
View File
@@ -115,6 +115,9 @@ class MSC3861OAuthDelegation(HomeserverTestCase):
config = super().default_config()
config["public_baseurl"] = BASE_URL
config["disable_registration"] = True
config["login_via_existing_session"] = {
"enabled": False,
}
config["experimental_features"] = {
"msc3861": {
"enabled": True,
+33 -11
View File
@@ -36,8 +36,9 @@ from tests.test_utils import make_awaitable
from tests.unittest import override_config
# Login flows we expect to appear in the list after the normal ones.
ADDITIONAL_LOGIN_FLOWS = [
ADDITIONAL_LOGIN_FLOWS: List[Dict] = [
{"type": "m.login.application_service"},
{"type": "m.login.token", "get_login_token": True},
]
# a mock instance which the dummy auth providers delegate to, so we can see what's going
@@ -45,6 +46,10 @@ ADDITIONAL_LOGIN_FLOWS = [
mock_password_provider = Mock()
def sort_flows(flows: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
return sorted(flows, key=lambda f: f["type"])
class LegacyPasswordOnlyAuthProvider:
"""A legacy password_provider which only implements `check_password`."""
@@ -184,7 +189,10 @@ class PasswordAuthProviderTests(unittest.HomeserverTestCase):
def password_only_auth_provider_login_test_body(self) -> None:
# login flows should only have m.login.password
flows = self._get_login_flows()
self.assertEqual(flows, [{"type": "m.login.password"}] + ADDITIONAL_LOGIN_FLOWS)
self.assertEqual(
sort_flows(flows),
sort_flows([{"type": "m.login.password"}] + ADDITIONAL_LOGIN_FLOWS),
)
# check_password must return an awaitable
mock_password_provider.check_password.return_value = make_awaitable(True)
@@ -365,7 +373,7 @@ class PasswordAuthProviderTests(unittest.HomeserverTestCase):
"""password auth doesn't work if it's disabled across the board"""
# login flows should be empty
flows = self._get_login_flows()
self.assertEqual(flows, ADDITIONAL_LOGIN_FLOWS)
self.assertEqual(sort_flows(flows), sort_flows(ADDITIONAL_LOGIN_FLOWS))
# login shouldn't work and should be rejected with a 400 ("unknown login type")
channel = self._send_password_login("u", "p")
@@ -386,9 +394,11 @@ class PasswordAuthProviderTests(unittest.HomeserverTestCase):
# (password must come first, because reasons)
flows = self._get_login_flows()
self.assertEqual(
flows,
[{"type": "m.login.password"}, {"type": "test.login_type"}]
+ ADDITIONAL_LOGIN_FLOWS,
sort_flows(flows),
sort_flows(
[{"type": "m.login.password"}, {"type": "test.login_type"}]
+ ADDITIONAL_LOGIN_FLOWS
),
)
# login with missing param should be rejected
@@ -519,7 +529,10 @@ class PasswordAuthProviderTests(unittest.HomeserverTestCase):
self.register_user("localuser", "localpass")
flows = self._get_login_flows()
self.assertEqual(flows, [{"type": "test.login_type"}] + ADDITIONAL_LOGIN_FLOWS)
self.assertEqual(
sort_flows(flows),
sort_flows([{"type": "test.login_type"}] + ADDITIONAL_LOGIN_FLOWS),
)
# login shouldn't work and should be rejected with a 400 ("unknown login type")
channel = self._send_password_login("localuser", "localpass")
@@ -554,7 +567,10 @@ class PasswordAuthProviderTests(unittest.HomeserverTestCase):
self.register_user("localuser", "localpass")
flows = self._get_login_flows()
self.assertEqual(flows, [{"type": "test.login_type"}] + ADDITIONAL_LOGIN_FLOWS)
self.assertEqual(
sort_flows(flows),
sort_flows([{"type": "test.login_type"}] + ADDITIONAL_LOGIN_FLOWS),
)
# login shouldn't work and should be rejected with a 400 ("unknown login type")
channel = self._send_password_login("localuser", "localpass")
@@ -585,7 +601,10 @@ class PasswordAuthProviderTests(unittest.HomeserverTestCase):
self.register_user("localuser", "localpass")
flows = self._get_login_flows()
self.assertEqual(flows, [{"type": "test.login_type"}] + ADDITIONAL_LOGIN_FLOWS)
self.assertEqual(
sort_flows(flows),
sort_flows([{"type": "test.login_type"}] + ADDITIONAL_LOGIN_FLOWS),
)
# login shouldn't work and should be rejected with a 400 ("unknown login type")
channel = self._send_password_login("localuser", "localpass")
@@ -690,7 +709,10 @@ class PasswordAuthProviderTests(unittest.HomeserverTestCase):
self.register_user("localuser", "localpass")
flows = self._get_login_flows()
self.assertEqual(flows, [{"type": "test.login_type"}] + ADDITIONAL_LOGIN_FLOWS)
self.assertEqual(
sort_flows(flows),
sort_flows([{"type": "test.login_type"}] + ADDITIONAL_LOGIN_FLOWS),
)
# password login shouldn't work and should be rejected with a 400
# ("unknown login type")
@@ -928,7 +950,7 @@ class PasswordAuthProviderTests(unittest.HomeserverTestCase):
self.assertEqual(channel.code, HTTPStatus.OK, channel.json_body)
return channel.json_body
def _get_login_flows(self) -> JsonDict:
def _get_login_flows(self) -> List[JsonDict]:
channel = self.make_request("GET", "/_matrix/client/r0/login")
self.assertEqual(channel.code, HTTPStatus.OK, channel.result)
return channel.json_body["flows"]
+2
View File
@@ -45,6 +45,7 @@ class JWKSTestCase(HomeserverTestCase):
@override_config(
{
"disable_registration": True,
"login_via_existing_session": {"enabled": False},
"experimental_features": {
"msc3861": {
"enabled": True,
@@ -65,6 +66,7 @@ class JWKSTestCase(HomeserverTestCase):
@override_config(
{
"disable_registration": True,
"login_via_existing_session": {"enabled": False},
"experimental_features": {
"msc3861": {
"enabled": True,
+1 -1
View File
@@ -187,6 +187,7 @@ class CapabilitiesTestCase(unittest.HomeserverTestCase):
for room_version in details["support"]:
self.assertTrue(room_version in KNOWN_ROOM_VERSIONS, str(room_version))
@override_config({"login_via_existing_session": {"enabled": False}})
def test_get_get_token_login_fields_when_disabled(self) -> None:
"""By default login via an existing session is disabled."""
access_token = self.get_success(
@@ -201,7 +202,6 @@ class CapabilitiesTestCase(unittest.HomeserverTestCase):
self.assertEqual(channel.code, HTTPStatus.OK)
self.assertFalse(capabilities["m.get_login_token"]["enabled"])
@override_config({"login_via_existing_session": {"enabled": True}})
def test_get_get_token_login_fields_when_enabled(self) -> None:
access_token = self.get_success(
self.auth_handler.create_access_token_for_user_id(
+1 -1
View File
@@ -446,6 +446,7 @@ class LoginRestServletTestCase(unittest.HomeserverTestCase):
ApprovalNoticeMedium.NONE, channel.json_body["approval_notice_medium"]
)
@override_config({"login_via_existing_session": {"enabled": False}})
def test_get_login_flows_with_login_via_existing_disabled(self) -> None:
"""GET /login should return m.login.token without get_login_token"""
channel = self.make_request("GET", "/_matrix/client/r0/login")
@@ -454,7 +455,6 @@ class LoginRestServletTestCase(unittest.HomeserverTestCase):
flows = {flow["type"]: flow for flow in channel.json_body["flows"]}
self.assertNotIn("m.login.token", flows)
@override_config({"login_via_existing_session": {"enabled": True}})
def test_get_login_flows_with_login_via_existing_enabled(self) -> None:
"""GET /login should return m.login.token with get_login_token true"""
channel = self.make_request("GET", "/_matrix/client/r0/login")
@@ -46,6 +46,7 @@ class LoginTokenRequestServletTestCase(unittest.HomeserverTestCase):
self.user = "user123"
self.password = "password"
@override_config({"login_via_existing_session": {"enabled": False}})
def test_disabled(self) -> None:
channel = self.make_request("POST", GET_TOKEN_ENDPOINT, {}, access_token=None)
self.assertEqual(channel.code, 404)
@@ -56,12 +57,10 @@ class LoginTokenRequestServletTestCase(unittest.HomeserverTestCase):
channel = self.make_request("POST", GET_TOKEN_ENDPOINT, {}, access_token=token)
self.assertEqual(channel.code, 404)
@override_config({"login_via_existing_session": {"enabled": True}})
def test_require_auth(self) -> None:
channel = self.make_request("POST", GET_TOKEN_ENDPOINT, {}, access_token=None)
self.assertEqual(channel.code, 401)
@override_config({"login_via_existing_session": {"enabled": True}})
def test_uia_on(self) -> None:
user_id = self.register_user(self.user, self.password)
token = self.login(self.user, self.password)
@@ -95,9 +94,7 @@ class LoginTokenRequestServletTestCase(unittest.HomeserverTestCase):
self.assertEqual(channel.code, 200, channel.result)
self.assertEqual(channel.json_body["user_id"], user_id)
@override_config(
{"login_via_existing_session": {"enabled": True, "require_ui_auth": False}}
)
@override_config({"login_via_existing_session": {"require_ui_auth": False}})
def test_uia_off(self) -> None:
user_id = self.register_user(self.user, self.password)
token = self.login(self.user, self.password)
@@ -119,7 +116,6 @@ class LoginTokenRequestServletTestCase(unittest.HomeserverTestCase):
@override_config(
{
"login_via_existing_session": {
"enabled": True,
"require_ui_auth": False,
"token_timeout": "15s",
}
@@ -136,7 +132,6 @@ class LoginTokenRequestServletTestCase(unittest.HomeserverTestCase):
@override_config(
{
"login_via_existing_session": {
"enabled": True,
"require_ui_auth": False,
"token_timeout": "15s",
}
+1
View File
@@ -119,6 +119,7 @@ class WellKnownTests(unittest.HomeserverTestCase):
},
},
"disable_registration": True,
"login_via_existing_session": {"enabled": False},
}
)
def test_client_well_known_msc3861_oauth_delegation(self) -> None: