From dd05cc55eedbf086ae224a13c9ae9f0332d96b1f Mon Sep 17 00:00:00 2001 From: Olivier D Date: Thu, 10 Apr 2025 15:39:27 +0200 Subject: Add passthrough_authorization_parameters support to OIDC configuration (#18232) # Add passthrough_authorization_parameters support to OIDC configuration This PR adds `the passthrough_authorization_parameters` option to OIDC configuration, allowing specific query parameters (like `login_hint`) to be passed from the redirect endpoint to the authorization grant URL. This enables clients to provide additional context to identity providers during authentication flows. # 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)) --------- Co-authored-by: Quentin Gliech --- changelog.d/18232.feature | 1 + docs/usage/configuration/config_documentation.md | 4 ++++ synapse/config/oidc.py | 6 ++++++ synapse/handlers/oidc.py | 12 ++++++++++- tests/handlers/test_oidc.py | 26 ++++++++++++++++++++++++ 5 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 changelog.d/18232.feature diff --git a/changelog.d/18232.feature b/changelog.d/18232.feature new file mode 100644 index 0000000000..ba5059ba80 --- /dev/null +++ b/changelog.d/18232.feature @@ -0,0 +1 @@ +Add `passthrough_authorization_parameters` in OIDC configuration to allow to pass parameters to the authorization grant URL. diff --git a/docs/usage/configuration/config_documentation.md b/docs/usage/configuration/config_documentation.md index d2d282f203..73fd9622ce 100644 --- a/docs/usage/configuration/config_documentation.md +++ b/docs/usage/configuration/config_documentation.md @@ -3672,6 +3672,9 @@ Options for each entry include: * `additional_authorization_parameters`: String to string dictionary that will be passed as additional parameters to the authorization grant URL. +* `passthrough_authorization_parameters`: List of parameters that will be passed through from the redirect endpoint + to the authorization grant URL. + * `allow_existing_users`: set to true to allow a user logging in via OIDC to match a pre-existing account instead of failing. This could be used if switching from password logins to OIDC. Defaults to false. @@ -3798,6 +3801,7 @@ oidc_providers: jwks_uri: "https://accounts.example.com/.well-known/jwks.json" additional_authorization_parameters: acr_values: 2fa + passthrough_authorization_parameters: ["login_hint"] skip_verification: true enable_registration: true user_mapping_provider: diff --git a/synapse/config/oidc.py b/synapse/config/oidc.py index 8ba0ba2c36..3ddf65a3e9 100644 --- a/synapse/config/oidc.py +++ b/synapse/config/oidc.py @@ -356,6 +356,9 @@ def _parse_oidc_config_dict( additional_authorization_parameters=oidc_config.get( "additional_authorization_parameters", {} ), + passthrough_authorization_parameters=oidc_config.get( + "passthrough_authorization_parameters", [] + ), ) @@ -501,3 +504,6 @@ class OidcProviderConfig: # Additional parameters that will be passed to the authorization grant URL additional_authorization_parameters: Mapping[str, str] + + # Allow query parameters to the redirect endpoint that will be passed to the authorization grant URL + passthrough_authorization_parameters: Collection[str] diff --git a/synapse/handlers/oidc.py b/synapse/handlers/oidc.py index 18efdd9f6e..c4cf0636a3 100644 --- a/synapse/handlers/oidc.py +++ b/synapse/handlers/oidc.py @@ -467,6 +467,10 @@ class OidcProvider: self._sso_handler.register_identity_provider(self) + self.passthrough_authorization_parameters = ( + provider.passthrough_authorization_parameters + ) + def _validate_metadata(self, m: OpenIDProviderMetadata) -> None: """Verifies the provider metadata. @@ -1005,7 +1009,6 @@ class OidcProvider: when everything is done (or None for UI Auth) ui_auth_session_id: The session ID of the ongoing UI Auth (or None if this is a login). - Returns: The redirect URL to the authorization endpoint. @@ -1078,6 +1081,13 @@ class OidcProvider: ) ) + # add passthrough additional authorization parameters + passthrough_authorization_parameters = self.passthrough_authorization_parameters + for parameter in passthrough_authorization_parameters: + parameter_value = parse_string(request, parameter) + if parameter_value: + additional_authorization_parameters.update({parameter: parameter_value}) + authorization_endpoint = metadata.get("authorization_endpoint") return prepare_grant_uri( authorization_endpoint, diff --git a/tests/handlers/test_oidc.py b/tests/handlers/test_oidc.py index cfd9969563..a7cead83d0 100644 --- a/tests/handlers/test_oidc.py +++ b/tests/handlers/test_oidc.py @@ -484,6 +484,32 @@ class OidcHandlerTestCase(HomeserverTestCase): self.assertEqual(code_verifier, "") self.assertEqual(redirect, "http://client/redirect") + @override_config( + { + "oidc_config": { + **DEFAULT_CONFIG, + "passthrough_authorization_parameters": ["additional_parameter"], + } + } + ) + def test_passthrough_parameters(self) -> None: + """The redirect request has additional parameters, one is authorized, one is not""" + req = Mock(spec=["cookies", "args"]) + req.cookies = [] + req.args = {} + req.args[b"additional_parameter"] = ["a_value".encode("utf-8")] + req.args[b"not_authorized_parameter"] = ["any".encode("utf-8")] + + url = urlparse( + self.get_success( + self.provider.handle_redirect_request(req, b"http://client/redirect") + ) + ) + + params = parse_qs(url.query) + self.assertEqual(params["additional_parameter"], ["a_value"]) + self.assertNotIn("not_authorized_parameters", params) + @override_config({"oidc_config": DEFAULT_CONFIG}) def test_redirect_request_with_code_challenge(self) -> None: """The redirect request has the right arguments & generates a valid session cookie.""" -- cgit 1.5.1