From 207f6560ce13d3d7d729fd26914f0be377e26ee6 Mon Sep 17 00:00:00 2001 From: Paul Chobert Date: Thu, 3 Sep 2026 16:07:51 +0200 Subject: [PATCH] Stabilize the `M_APPSERVICE_LOGIN_UNSUPPORTED` error code (#20180) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Part of: https://github.com/element-hq/synapse/issues/19415 Return `M_APPSERVICE_LOGIN_UNSUPPORTED` error code instead of the unstable `IO.ELEMENT.MSC4190.M_APPSERVICE_LOGIN_UNSUPPORTED` identifier. > Servers MUST still allow application services to use the `/register` endpoint with a login type of `m.login.application_service` even if they don't support the Legacy Authentication API. In that case application services MUST set the `"inhibit_login": true` parameter as they cannot use it to log in as users. If the `inhibit_login` parameter is not set to `true`, the server MUST return a 400 HTTP status code with an `M_APPSERVICE_LOGIN_UNSUPPORTED` error code. > > [...] > > Application services MUST NOT use the `/login` endpoint if the server doesn't support the Legacy authentication API. If `/login` is called with the `m.login.application_service` login type the server MUST return a 400 HTTP status code with an `M_APPSERVICE_LOGIN_UNSUPPORTED` error code. > > — [Matrix v1.19, Application Service API](https://spec.matrix.org/v1.19/application-service-api/#registration) Synapse returns the correct 400 on both endpoints, but with the unstable identifier. Before: ``` POST /_matrix/client/v3/login {"type": "m.login.application_service", ...} # appservice with MSC4190 device management POST /_matrix/client/v3/register {"type": "m.login.application_service", ...} # without "inhibit_login": true 400 {"errcode": "IO.ELEMENT.MSC4190.M_APPSERVICE_LOGIN_UNSUPPORTED"} ``` After: ``` POST /_matrix/client/v3/login {"type": "m.login.application_service", ...} # appservice with MSC4190 device management POST /_matrix/client/v3/register {"type": "m.login.application_service", ...} # without "inhibit_login": true 400 {"errcode": "M_APPSERVICE_LOGIN_UNSUPPORTED"} ``` A Sister PR exists in MAS: https://github.com/element-hq/matrix-authentication-service/pull/5961 ; when delegation is enabled, `/login` reaches MAS instead of Synapse, and MAS currently answers `m.login.application_service` with `M_UNKNOWN`. --- changelog.d/20180.bugfix | 1 + synapse/api/errors.py | 2 +- tests/handlers/test_oauth_delegation.py | 28 +++++++++++++++++++++++++ tests/rest/client/test_login.py | 4 ++-- tests/rest/client/test_register.py | 2 +- 5 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 changelog.d/20180.bugfix diff --git a/changelog.d/20180.bugfix b/changelog.d/20180.bugfix new file mode 100644 index 0000000000..72f4fc51d0 --- /dev/null +++ b/changelog.d/20180.bugfix @@ -0,0 +1 @@ +Return the stable `M_APPSERVICE_LOGIN_UNSUPPORTED` error code, added in Matrix 1.17, instead of its unstable MSC4190-prefixed identifier. diff --git a/synapse/api/errors.py b/synapse/api/errors.py index 78fd8c3a60..00b5fa9a76 100644 --- a/synapse/api/errors.py +++ b/synapse/api/errors.py @@ -143,7 +143,7 @@ class Codes(str, Enum): INVITE_BLOCKED = "M_INVITE_BLOCKED" # Part of MSC4190 - APPSERVICE_LOGIN_UNSUPPORTED = "IO.ELEMENT.MSC4190.M_APPSERVICE_LOGIN_UNSUPPORTED" + APPSERVICE_LOGIN_UNSUPPORTED = "M_APPSERVICE_LOGIN_UNSUPPORTED" # Part of MSC4306: Thread Subscriptions MSC4306_CONFLICTING_UNSUBSCRIPTION = ( diff --git a/tests/handlers/test_oauth_delegation.py b/tests/handlers/test_oauth_delegation.py index 995a1134b2..71f83607ff 100644 --- a/tests/handlers/test_oauth_delegation.py +++ b/tests/handlers/test_oauth_delegation.py @@ -587,6 +587,34 @@ class DisabledEndpointsTestCase(HomeserverTestCase): "POST", "/_matrix/client/v3/register/msisdn/requestToken" ) + def test_appservice_registration_without_inhibit_login(self) -> None: + """Test that appservice registration without `inhibit_login` is rejected + with the `M_APPSERVICE_LOGIN_UNSUPPORTED` errcode.""" + appservice = ApplicationService( + token="i_am_an_app_service", + id="1234", + namespaces={"users": [{"regex": r"@alice:.+", "exclusive": True}]}, + sender=UserID.from_string("@as_main:test"), + ) + + self.hs.get_datastores().main.services_cache = [appservice] + channel = self.make_request( + "POST", + "/_matrix/client/v3/register", + { + "username": "alice", + "type": "m.login.application_service", + }, + shorthand=False, + access_token="i_am_an_app_service", + ) + self.assertEqual(channel.code, 400, channel.json_body) + self.assertEqual( + channel.json_body.get("errcode"), + "M_APPSERVICE_LOGIN_UNSUPPORTED", + channel.json_body, + ) + def test_session_management_endpoints_removed(self) -> None: """Test that session management endpoints that were removed in MSC2964 are no longer available.""" self.expect_unrecognized("GET", "/_matrix/client/v3/login") diff --git a/tests/rest/client/test_login.py b/tests/rest/client/test_login.py index d83604a696..f1639bfeee 100644 --- a/tests/rest/client/test_login.py +++ b/tests/rest/client/test_login.py @@ -1521,7 +1521,7 @@ class AppserviceLoginRestServletTestCase(unittest.HomeserverTestCase): self.assertEqual(channel.code, 200, msg=channel.result) def test_login_appservice_msc4190_fail(self) -> None: - """Test that an appservice user can use /login""" + """Test that an appservice with MSC4190 enabled can't use /login""" self.register_appservice_user( "as3_user_alice", self.msc4190_service.token, inhibit_login=True ) @@ -1537,7 +1537,7 @@ class AppserviceLoginRestServletTestCase(unittest.HomeserverTestCase): self.assertEqual(channel.code, 400, msg=channel.result) self.assertEqual( channel.json_body.get("errcode"), - Codes.APPSERVICE_LOGIN_UNSUPPORTED, + "M_APPSERVICE_LOGIN_UNSUPPORTED", channel.json_body, ) diff --git a/tests/rest/client/test_register.py b/tests/rest/client/test_register.py index 1b04ea9d5c..67f78ee24f 100644 --- a/tests/rest/client/test_register.py +++ b/tests/rest/client/test_register.py @@ -183,7 +183,7 @@ class RegisterRestServletTestCase(unittest.HomeserverTestCase): self.assertEqual(channel.code, 400, channel.json_body) self.assertEqual( channel.json_body.get("errcode"), - Codes.APPSERVICE_LOGIN_UNSUPPORTED, + "M_APPSERVICE_LOGIN_UNSUPPORTED", channel.json_body, )