From bc428990086a473e8c3b9bad77ef4d899fbca93a Mon Sep 17 00:00:00 2001 From: Devon Hudson Date: Tue, 18 Nov 2025 18:45:33 +0000 Subject: [PATCH] Allow subpaths in MAS endpoints (#19186) Fixes #19184 ### Pull Request Checklist * [X] Pull request is based on the develop branch * [X] Pull request includes a [changelog file](https://element-hq.github.io/synapse/latest/development/contributing_guide.html#changelog). The entry should: - Be a short description of your change which makes sense to users. "Fixed a bug that prevented receiving messages from other servers." instead of "Moved X method from `EventStore` to `EventWorkerStore`.". - Use markdown where necessary, mostly for `code blocks`. - End with either a period (.) or an exclamation mark (!). - Start with a capital letter. - Feel free to credit yourself, by adding a sentence "Contributed by @github_username." or "Contributed by [Your Name]." to the end of the entry. * [X] [Code style](https://element-hq.github.io/synapse/latest/code_style.html) is correct (run the [linters](https://element-hq.github.io/synapse/latest/development/contributing_guide.html#run-the-linters)) --- changelog.d/19186.bugfix | 1 + synapse/api/auth/mas.py | 27 +++---------------------- tests/handlers/test_oauth_delegation.py | 26 ++++++++++++++++++++++++ 3 files changed, 30 insertions(+), 24 deletions(-) create mode 100644 changelog.d/19186.bugfix diff --git a/changelog.d/19186.bugfix b/changelog.d/19186.bugfix new file mode 100644 index 000000000..e5ef1bf6d --- /dev/null +++ b/changelog.d/19186.bugfix @@ -0,0 +1 @@ +Fix regression preventing subpaths in MAS endpoints. diff --git a/synapse/api/auth/mas.py b/synapse/api/auth/mas.py index e422a1e5c..c4cca3723 100644 --- a/synapse/api/auth/mas.py +++ b/synapse/api/auth/mas.py @@ -17,7 +17,6 @@ from typing import TYPE_CHECKING from urllib.parse import urlencode from pydantic import ( - AnyHttpUrl, BaseModel, ConfigDict, StrictBool, @@ -147,33 +146,13 @@ class MasDelegatedAuth(BaseAuth): @property def _metadata_url(self) -> str: - return str( - AnyHttpUrl.build( - scheme=self._config.endpoint.scheme, - username=self._config.endpoint.username, - password=self._config.endpoint.password, - host=self._config.endpoint.host or "", - port=self._config.endpoint.port, - path=".well-known/openid-configuration", - query=None, - fragment=None, - ) + return ( + f"{str(self._config.endpoint).rstrip('/')}/.well-known/openid-configuration" ) @property def _introspection_endpoint(self) -> str: - return str( - AnyHttpUrl.build( - scheme=self._config.endpoint.scheme, - username=self._config.endpoint.username, - password=self._config.endpoint.password, - host=self._config.endpoint.host or "", - port=self._config.endpoint.port, - path="oauth2/introspect", - query=None, - fragment=None, - ) - ) + return f"{str(self._config.endpoint).rstrip('/')}/oauth2/introspect" async def _load_metadata(self) -> ServerMetadata: response = await self._http_client.get_json(self._metadata_url) diff --git a/tests/handlers/test_oauth_delegation.py b/tests/handlers/test_oauth_delegation.py index c0a197874..b04aeffb9 100644 --- a/tests/handlers/test_oauth_delegation.py +++ b/tests/handlers/test_oauth_delegation.py @@ -1057,6 +1057,32 @@ class MasAuthDelegation(HomeserverTestCase): self.assertEqual(self.server.calls, 1) +class MasAuthDelegationWithSubpath(MasAuthDelegation): + """Test MAS delegation when the MAS server is hosted on a subpath.""" + + def default_config(self) -> dict[str, Any]: + config = super().default_config() + # Override the endpoint to include a subpath + config["matrix_authentication_service"]["endpoint"] = ( + self.server.endpoint + "auth/path/" + ) + return config + + def test_introspection_endpoint_uses_subpath(self) -> None: + """Test that the introspection endpoint correctly uses the configured subpath.""" + expected_introspection_url = ( + self.server.endpoint + "auth/path/oauth2/introspect" + ) + self.assertEqual(self._auth._introspection_endpoint, expected_introspection_url) + + def test_metadata_url_uses_subpath(self) -> None: + """Test that the metadata URL correctly uses the configured subpath.""" + expected_metadata_url = ( + self.server.endpoint + "auth/path/.well-known/openid-configuration" + ) + self.assertEqual(self._auth._metadata_url, expected_metadata_url) + + @parameterized_class( ("config",), [