Stabilize support for MSC4326: Device masquerading for appservices (#19033)

Note: the code references MSC3202, which is what MSC4326 was split off
from. Only MSC4326 was accepted, MSC3202 wasn't yet.
This commit is contained in:
Tulir Asokan 2025-10-13 19:13:07 +03:00 committed by GitHub
parent d2c582ef3c
commit ec7554b768
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 13 additions and 22 deletions

View File

@ -0,0 +1 @@
Stabilized support for [MSC4326](https://github.com/matrix-org/matrix-spec-proposals/pull/4326): Device masquerading for appservices. Contributed by @tulir @ Beeper.

View File

@ -302,12 +302,9 @@ class BaseAuth:
(the user_id URI parameter allows an application service to masquerade (the user_id URI parameter allows an application service to masquerade
any applicable user in its namespace) any applicable user in its namespace)
- what device the application service should be treated as controlling - what device the application service should be treated as controlling
(the device_id[^1] URI parameter allows an application service to masquerade (the device_id URI parameter allows an application service to masquerade
as any device that exists for the relevant user) as any device that exists for the relevant user)
[^1] Unstable and provided by MSC3202.
Must use `org.matrix.msc3202.device_id` in place of `device_id` for now.
Returns: Returns:
the application service `Requester` of that request the application service `Requester` of that request
@ -319,7 +316,8 @@ class BaseAuth:
- The returned device ID, if present, has been checked to be a valid device ID - The returned device ID, if present, has been checked to be a valid device ID
for the returned user ID. for the returned user ID.
""" """
DEVICE_ID_ARG_NAME = b"org.matrix.msc3202.device_id" # TODO: We can drop unstable support after 2026-01-01 (couple months after stable support)
UNSTABLE_DEVICE_ID_ARG_NAME = b"org.matrix.msc3202.device_id"
app_service = self.store.get_app_service_by_token(access_token) app_service = self.store.get_app_service_by_token(access_token)
if app_service is None: if app_service is None:
@ -341,13 +339,11 @@ class BaseAuth:
else: else:
effective_user_id = app_service.sender effective_user_id = app_service.sender
effective_device_id: Optional[str] = None effective_device_id_args = request.args.get(
b"device_id", request.args.get(UNSTABLE_DEVICE_ID_ARG_NAME)
if ( )
self.hs.config.experimental.msc3202_device_masquerading_enabled if effective_device_id_args:
and DEVICE_ID_ARG_NAME in request.args effective_device_id = effective_device_id_args[0].decode("utf8")
):
effective_device_id = request.args[DEVICE_ID_ARG_NAME][0].decode("utf8")
# We only just set this so it can't be None! # We only just set this so it can't be None!
assert effective_device_id is not None assert effective_device_id is not None
device_opt = await self.store.get_device( device_opt = await self.store.get_device(
@ -359,6 +355,8 @@ class BaseAuth:
f"Application service trying to use a device that doesn't exist ('{effective_device_id}' for {effective_user_id})", f"Application service trying to use a device that doesn't exist ('{effective_device_id}' for {effective_user_id})",
Codes.UNKNOWN_DEVICE, Codes.UNKNOWN_DEVICE,
) )
else:
effective_device_id = None
return create_requester( return create_requester(
effective_user_id, app_service=app_service, device_id=effective_device_id effective_user_id, app_service=app_service, device_id=effective_device_id

View File

@ -412,11 +412,6 @@ class ExperimentalConfig(Config):
"msc2409_to_device_messages_enabled", False "msc2409_to_device_messages_enabled", False
) )
# The portion of MSC3202 which is related to device masquerading.
self.msc3202_device_masquerading_enabled: bool = experimental.get(
"msc3202_device_masquerading", False
)
# The portion of MSC3202 related to transaction extensions: # The portion of MSC3202 related to transaction extensions:
# sending device list changes, one-time key counts and fallback key # sending device list changes, one-time key counts and fallback key
# usage to application services. # usage to application services.

View File

@ -42,7 +42,6 @@ from synapse.types import Requester, UserID
from synapse.util.clock import Clock from synapse.util.clock import Clock
from tests import unittest from tests import unittest
from tests.unittest import override_config
from tests.utils import mock_getRawHeaders from tests.utils import mock_getRawHeaders
@ -237,7 +236,6 @@ class AuthTestCase(unittest.HomeserverTestCase):
request.requestHeaders.getRawHeaders = mock_getRawHeaders() request.requestHeaders.getRawHeaders = mock_getRawHeaders()
self.get_failure(self.auth.get_user_by_req(request), AuthError) self.get_failure(self.auth.get_user_by_req(request), AuthError)
@override_config({"experimental_features": {"msc3202_device_masquerading": True}})
def test_get_user_by_req_appservice_valid_token_valid_device_id(self) -> None: def test_get_user_by_req_appservice_valid_token_valid_device_id(self) -> None:
""" """
Tests that when an application service passes the device_id URL parameter Tests that when an application service passes the device_id URL parameter
@ -264,7 +262,7 @@ class AuthTestCase(unittest.HomeserverTestCase):
request.getClientAddress.return_value.host = "127.0.0.1" request.getClientAddress.return_value.host = "127.0.0.1"
request.args[b"access_token"] = [self.test_token] request.args[b"access_token"] = [self.test_token]
request.args[b"user_id"] = [masquerading_user_id] request.args[b"user_id"] = [masquerading_user_id]
request.args[b"org.matrix.msc3202.device_id"] = [masquerading_device_id] request.args[b"device_id"] = [masquerading_device_id]
request.requestHeaders.getRawHeaders = mock_getRawHeaders() request.requestHeaders.getRawHeaders = mock_getRawHeaders()
requester = self.get_success(self.auth.get_user_by_req(request)) requester = self.get_success(self.auth.get_user_by_req(request))
self.assertEqual( self.assertEqual(
@ -272,7 +270,6 @@ class AuthTestCase(unittest.HomeserverTestCase):
) )
self.assertEqual(requester.device_id, masquerading_device_id.decode("utf8")) self.assertEqual(requester.device_id, masquerading_device_id.decode("utf8"))
@override_config({"experimental_features": {"msc3202_device_masquerading": True}})
def test_get_user_by_req_appservice_valid_token_invalid_device_id(self) -> None: def test_get_user_by_req_appservice_valid_token_invalid_device_id(self) -> None:
""" """
Tests that when an application service passes the device_id URL parameter Tests that when an application service passes the device_id URL parameter
@ -299,7 +296,7 @@ class AuthTestCase(unittest.HomeserverTestCase):
request.getClientAddress.return_value.host = "127.0.0.1" request.getClientAddress.return_value.host = "127.0.0.1"
request.args[b"access_token"] = [self.test_token] request.args[b"access_token"] = [self.test_token]
request.args[b"user_id"] = [masquerading_user_id] request.args[b"user_id"] = [masquerading_user_id]
request.args[b"org.matrix.msc3202.device_id"] = [masquerading_device_id] request.args[b"device_id"] = [masquerading_device_id]
request.requestHeaders.getRawHeaders = mock_getRawHeaders() request.requestHeaders.getRawHeaders = mock_getRawHeaders()
failure = self.get_failure(self.auth.get_user_by_req(request), AuthError) failure = self.get_failure(self.auth.get_user_by_req(request), AuthError)