summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--changelog.d/17086.feature1
-rw-r--r--synapse/config/experimental.py11
-rw-r--r--synapse/http/server.py13
-rw-r--r--synapse/rest/client/rendezvous.py30
-rw-r--r--synapse/rest/client/versions.py3
-rw-r--r--tests/rest/client/test_rendezvous.py34
6 files changed, 84 insertions, 8 deletions
diff --git a/changelog.d/17086.feature b/changelog.d/17086.feature
new file mode 100644
index 0000000000..08b407d316
--- /dev/null
+++ b/changelog.d/17086.feature
@@ -0,0 +1 @@
+Support delegating the rendezvous mechanism described MSC4108 to an external implementation.
diff --git a/synapse/config/experimental.py b/synapse/config/experimental.py
index fcc78d2d81..353ae23f91 100644
--- a/synapse/config/experimental.py
+++ b/synapse/config/experimental.py
@@ -411,3 +411,14 @@ class ExperimentalConfig(Config):
         self.msc4069_profile_inhibit_propagation = experimental.get(
             "msc4069_profile_inhibit_propagation", False
         )
+
+        # MSC4108: Mechanism to allow OIDC sign in and E2EE set up via QR code
+        self.msc4108_delegation_endpoint: Optional[str] = experimental.get(
+            "msc4108_delegation_endpoint", None
+        )
+
+        if self.msc4108_delegation_endpoint is not None and not self.msc3861.enabled:
+            raise ConfigError(
+                "MSC4108 requires MSC3861 to be enabled",
+                ("experimental", "msc4108_delegation_endpoint"),
+            )
diff --git a/synapse/http/server.py b/synapse/http/server.py
index c76500e14f..45b2cbffcd 100644
--- a/synapse/http/server.py
+++ b/synapse/http/server.py
@@ -909,7 +909,18 @@ def set_cors_headers(request: "SynapseRequest") -> None:
     request.setHeader(
         b"Access-Control-Allow-Methods", b"GET, HEAD, POST, PUT, DELETE, OPTIONS"
     )
-    if request.experimental_cors_msc3886:
+    if request.path is not None and request.path.startswith(
+        b"/_matrix/client/unstable/org.matrix.msc4108/rendezvous"
+    ):
+        request.setHeader(
+            b"Access-Control-Allow-Headers",
+            b"Content-Type, If-Match, If-None-Match",
+        )
+        request.setHeader(
+            b"Access-Control-Expose-Headers",
+            b"Synapse-Trace-Id, Server, ETag",
+        )
+    elif request.experimental_cors_msc3886:
         request.setHeader(
             b"Access-Control-Allow-Headers",
             b"X-Requested-With, Content-Type, Authorization, Date, If-Match, If-None-Match",
diff --git a/synapse/rest/client/rendezvous.py b/synapse/rest/client/rendezvous.py
index dee7c37ec5..ed06a29987 100644
--- a/synapse/rest/client/rendezvous.py
+++ b/synapse/rest/client/rendezvous.py
@@ -2,7 +2,7 @@
 # This file is licensed under the Affero General Public License (AGPL) version 3.
 #
 # Copyright 2022 The Matrix.org Foundation C.I.C.
-# Copyright (C) 2023 New Vector, Ltd
+# Copyright (C) 2023-2024 New Vector, Ltd
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Affero General Public License as
@@ -34,7 +34,7 @@ if TYPE_CHECKING:
 logger = logging.getLogger(__name__)
 
 
-class RendezvousServlet(RestServlet):
+class MSC3886RendezvousServlet(RestServlet):
     """
     This is a placeholder implementation of [MSC3886](https://github.com/matrix-org/matrix-spec-proposals/pull/3886)
     simple client rendezvous capability that is used by the "Sign in with QR" functionality.
@@ -76,6 +76,30 @@ class RendezvousServlet(RestServlet):
     # PUT, GET and DELETE are not implemented as they should be fulfilled by the redirect target.
 
 
+class MSC4108DelegationRendezvousServlet(RestServlet):
+    PATTERNS = client_patterns(
+        "/org.matrix.msc4108/rendezvous$", releases=[], v1=False, unstable=True
+    )
+
+    def __init__(self, hs: "HomeServer"):
+        super().__init__()
+        redirection_target: Optional[str] = (
+            hs.config.experimental.msc4108_delegation_endpoint
+        )
+        assert (
+            redirection_target is not None
+        ), "Servlet is only registered if there is a delegation target"
+        self.endpoint = redirection_target.encode("utf-8")
+
+    async def on_POST(self, request: SynapseRequest) -> None:
+        respond_with_redirect(
+            request, self.endpoint, statusCode=TEMPORARY_REDIRECT, cors=True
+        )
+
+
 def register_servlets(hs: "HomeServer", http_server: HttpServer) -> None:
     if hs.config.experimental.msc3886_endpoint is not None:
-        RendezvousServlet(hs).register(http_server)
+        MSC3886RendezvousServlet(hs).register(http_server)
+
+    if hs.config.experimental.msc4108_delegation_endpoint is not None:
+        MSC4108DelegationRendezvousServlet(hs).register(http_server)
diff --git a/synapse/rest/client/versions.py b/synapse/rest/client/versions.py
index c46d4fe8cf..638d4c45ae 100644
--- a/synapse/rest/client/versions.py
+++ b/synapse/rest/client/versions.py
@@ -140,6 +140,9 @@ class VersionsRestServlet(RestServlet):
                     "org.matrix.msc4069": self.config.experimental.msc4069_profile_inhibit_propagation,
                     # Allows clients to handle push for encrypted events.
                     "org.matrix.msc4028": self.config.experimental.msc4028_push_encrypted_events,
+                    # MSC4108: Mechanism to allow OIDC sign in and E2EE set up via QR code
+                    "org.matrix.msc4108": self.config.experimental.msc4108_delegation_endpoint
+                    is not None,
                 },
             },
         )
diff --git a/tests/rest/client/test_rendezvous.py b/tests/rest/client/test_rendezvous.py
index 294b39f179..c84704c090 100644
--- a/tests/rest/client/test_rendezvous.py
+++ b/tests/rest/client/test_rendezvous.py
@@ -27,8 +27,10 @@ from synapse.util import Clock
 
 from tests import unittest
 from tests.unittest import override_config
+from tests.utils import HAS_AUTHLIB
 
-endpoint = "/_matrix/client/unstable/org.matrix.msc3886/rendezvous"
+msc3886_endpoint = "/_matrix/client/unstable/org.matrix.msc3886/rendezvous"
+msc4108_endpoint = "/_matrix/client/unstable/org.matrix.msc4108/rendezvous"
 
 
 class RendezvousServletTestCase(unittest.HomeserverTestCase):
@@ -41,11 +43,35 @@ class RendezvousServletTestCase(unittest.HomeserverTestCase):
         return self.hs
 
     def test_disabled(self) -> None:
-        channel = self.make_request("POST", endpoint, {}, access_token=None)
+        channel = self.make_request("POST", msc3886_endpoint, {}, access_token=None)
+        self.assertEqual(channel.code, 404)
+        channel = self.make_request("POST", msc4108_endpoint, {}, access_token=None)
         self.assertEqual(channel.code, 404)
 
     @override_config({"experimental_features": {"msc3886_endpoint": "/asd"}})
-    def test_redirect(self) -> None:
-        channel = self.make_request("POST", endpoint, {}, access_token=None)
+    def test_msc3886_redirect(self) -> None:
+        channel = self.make_request("POST", msc3886_endpoint, {}, access_token=None)
         self.assertEqual(channel.code, 307)
         self.assertEqual(channel.headers.getRawHeaders("Location"), ["/asd"])
+
+    @unittest.skip_unless(HAS_AUTHLIB, "requires authlib")
+    @override_config(
+        {
+            "disable_registration": True,
+            "experimental_features": {
+                "msc4108_delegation_endpoint": "https://asd",
+                "msc3861": {
+                    "enabled": True,
+                    "issuer": "https://issuer",
+                    "client_id": "client_id",
+                    "client_auth_method": "client_secret_post",
+                    "client_secret": "client_secret",
+                    "admin_token": "admin_token_value",
+                },
+            },
+        }
+    )
+    def test_msc4108_delegation(self) -> None:
+        channel = self.make_request("POST", msc4108_endpoint, {}, access_token=None)
+        self.assertEqual(channel.code, 307)
+        self.assertEqual(channel.headers.getRawHeaders("Location"), ["https://asd"])