diff --git a/CHANGELOG.md b/CHANGELOG.md index 371abb56c..3dfa94c4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed * #1443 Query strings with invalid hex values now raise a SuspiciousOperation exception (in DRF extension) instead of raising a 500 ValueError: Invalid hex encoding in query string. +* #1468 `ui_locales` request parameter triggers `AttributeError` under certain circumstances ### Security ## [2.4.0] - 2024-05-13 diff --git a/oauth2_provider/views/base.py b/oauth2_provider/views/base.py index d2644f35f..1e0d12dea 100644 --- a/oauth2_provider/views/base.py +++ b/oauth2_provider/views/base.py @@ -186,6 +186,10 @@ def get(self, request, *args, **kwargs): # a successful response depending on "approval_prompt" url parameter require_approval = request.GET.get("approval_prompt", oauth2_settings.REQUEST_APPROVAL_PROMPT) + if "ui_locales" in credentials and isinstance(credentials["ui_locales"], list): + # Make sure ui_locales a space separated string for oauthlib to handle it correctly. + credentials["ui_locales"] = " ".join(credentials["ui_locales"]) + try: # If skip_authorization field is True, skip the authorization screen even # if this is the first use of the application and there was no previous authorization. diff --git a/tests/test_ui_locales.py b/tests/test_ui_locales.py new file mode 100644 index 000000000..d375dc55c --- /dev/null +++ b/tests/test_ui_locales.py @@ -0,0 +1,57 @@ +from django.contrib.auth import get_user_model +from django.test import TestCase, override_settings +from django.urls import reverse + +from oauth2_provider.models import get_application_model + + +UserModel = get_user_model() +Application = get_application_model() + + +@override_settings( + OAUTH2_PROVIDER={ + "OIDC_ENABLED": True, + "PKCE_REQUIRED": False, + "SCOPES": { + "openid": "OpenID connect", + }, + } +) +class TestUILocalesParam(TestCase): + @classmethod + def setUpTestData(cls): + cls.application = Application.objects.create( + name="Test Application", + client_id="test", + redirect_uris="https://www.example.com/", + client_type=Application.CLIENT_PUBLIC, + authorization_grant_type=Application.GRANT_AUTHORIZATION_CODE, + ) + cls.trusted_application = Application.objects.create( + name="Trusted Application", + client_id="trusted", + redirect_uris="https://www.example.com/", + client_type=Application.CLIENT_PUBLIC, + authorization_grant_type=Application.GRANT_AUTHORIZATION_CODE, + skip_authorization=True, + ) + cls.user = UserModel.objects.create_user("test_user") + cls.url = reverse("oauth2_provider:authorize") + + def setUp(self): + self.client.force_login(self.user) + + def test_application_ui_locales_param(self): + response = self.client.get( + f"{self.url}?response_type=code&client_id=test&scope=openid&ui_locales=de", + ) + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed(response, "oauth2_provider/authorize.html") + + def test_trusted_application_ui_locales_param(self): + response = self.client.get( + f"{self.url}?response_type=code&client_id=trusted&scope=openid&ui_locales=de", + ) + self.assertEqual(response.status_code, 302) + self.assertRegex(response.url, r"https://www\.example\.com/\?code=[a-zA-Z0-9]+")