Co-authored-by: Quentin Gliech <quenting@element.io>pull/16737/head
@@ -0,0 +1 @@ | |||
Update the implementation of [MSC2965](https://github.com/matrix-org/matrix-spec-proposals/pull/2965) (OIDC Provider discovery). |
@@ -22,6 +22,7 @@ from synapse.rest.client import ( | |||
account_validity, | |||
appservice_ping, | |||
auth, | |||
auth_issuer, | |||
capabilities, | |||
devices, | |||
directory, | |||
@@ -148,3 +149,4 @@ class ClientRestResource(JsonResource): | |||
mutual_rooms.register_servlets(hs, client_resource) | |||
login_token_request.register_servlets(hs, client_resource) | |||
rendezvous.register_servlets(hs, client_resource) | |||
auth_issuer.register_servlets(hs, client_resource) |
@@ -0,0 +1,63 @@ | |||
# Copyright 2023 The Matrix.org Foundation C.I.C. | |||
# | |||
# Licensed under the Apache License, Version 2.0 (the "License"); | |||
# you may not use this file except in compliance with the License. | |||
# You may obtain a copy of the License at | |||
# | |||
# http://www.apache.org/licenses/LICENSE-2.0 | |||
# | |||
# Unless required by applicable law or agreed to in writing, software | |||
# distributed under the License is distributed on an "AS IS" BASIS, | |||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
# See the License for the specific language governing permissions and | |||
# limitations under the License. | |||
import logging | |||
import typing | |||
from typing import Tuple | |||
from synapse.api.errors import Codes, SynapseError | |||
from synapse.http.server import HttpServer | |||
from synapse.http.servlet import RestServlet | |||
from synapse.http.site import SynapseRequest | |||
from synapse.rest.client._base import client_patterns | |||
from synapse.types import JsonDict | |||
if typing.TYPE_CHECKING: | |||
from synapse.server import HomeServer | |||
logger = logging.getLogger(__name__) | |||
class AuthIssuerServlet(RestServlet): | |||
""" | |||
Advertises what OpenID Connect issuer clients should use to authorise users. | |||
""" | |||
PATTERNS = client_patterns( | |||
"/org.matrix.msc2965/auth_issuer$", | |||
unstable=True, | |||
releases=(), | |||
) | |||
def __init__(self, hs: "HomeServer"): | |||
super().__init__() | |||
self._config = hs.config | |||
async def on_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]: | |||
if self._config.experimental.msc3861.enabled: | |||
return 200, {"issuer": self._config.experimental.msc3861.issuer} | |||
else: | |||
# Wouldn't expect this to be reached: the servelet shouldn't have been | |||
# registered. Still, fail gracefully if we are registered for some reason. | |||
raise SynapseError( | |||
404, | |||
"OIDC discovery has not been configured on this homeserver", | |||
Codes.NOT_FOUND, | |||
) | |||
def register_servlets(hs: "HomeServer", http_server: HttpServer) -> None: | |||
# We use the MSC3861 values as they are used by multiple MSCs | |||
if hs.config.experimental.msc3861.enabled: | |||
AuthIssuerServlet(hs).register(http_server) |
@@ -0,0 +1,59 @@ | |||
# Copyright 2023 The Matrix.org Foundation C.I.C. | |||
# | |||
# Licensed under the Apache License, Version 2.0 (the "License"); | |||
# you may not use this file except in compliance with the License. | |||
# You may obtain a copy of the License at | |||
# | |||
# http://www.apache.org/licenses/LICENSE-2.0 | |||
# | |||
# Unless required by applicable law or agreed to in writing, software | |||
# distributed under the License is distributed on an "AS IS" BASIS, | |||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
# See the License for the specific language governing permissions and | |||
# limitations under the License. | |||
from http import HTTPStatus | |||
from synapse.rest.client import auth_issuer | |||
from tests.unittest import HomeserverTestCase, override_config, skip_unless | |||
from tests.utils import HAS_AUTHLIB | |||
ISSUER = "https://account.example.com/" | |||
class AuthIssuerTestCase(HomeserverTestCase): | |||
servlets = [ | |||
auth_issuer.register_servlets, | |||
] | |||
def test_returns_404_when_msc3861_disabled(self) -> None: | |||
# Make an unauthenticated request for the discovery info. | |||
channel = self.make_request( | |||
"GET", | |||
"/_matrix/client/unstable/org.matrix.msc2965/auth_issuer", | |||
) | |||
self.assertEqual(channel.code, HTTPStatus.NOT_FOUND) | |||
@skip_unless(HAS_AUTHLIB, "requires authlib") | |||
@override_config( | |||
{ | |||
"disable_registration": True, | |||
"experimental_features": { | |||
"msc3861": { | |||
"enabled": True, | |||
"issuer": ISSUER, | |||
"client_id": "David Lister", | |||
"client_auth_method": "client_secret_post", | |||
"client_secret": "Who shot Mister Burns?", | |||
} | |||
}, | |||
} | |||
) | |||
def test_returns_issuer_when_oidc_enabled(self) -> None: | |||
# Make an unauthenticated request for the discovery info. | |||
channel = self.make_request( | |||
"GET", | |||
"/_matrix/client/unstable/org.matrix.msc2965/auth_issuer", | |||
) | |||
self.assertEqual(channel.code, HTTPStatus.OK) | |||
self.assertEqual(channel.json_body, {"issuer": ISSUER}) |