From 7fcd1988c5e51b3449c83f9047ed6c91ea576ee4 Mon Sep 17 00:00:00 2001 From: Fox Danger Piacenti Date: Wed, 6 Sep 2023 13:14:48 -0500 Subject: [PATCH] feat: Allow trusted apps to perform cookie login. --- .../djangoapps/auth_exchange/tests/test_views.py | 12 ++++++++++-- openedx/core/djangoapps/auth_exchange/views.py | 16 +++++++++++----- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/openedx/core/djangoapps/auth_exchange/tests/test_views.py b/openedx/core/djangoapps/auth_exchange/tests/test_views.py index 9d8f21e6eed5..0ab08ffe37d8 100644 --- a/openedx/core/djangoapps/auth_exchange/tests/test_views.py +++ b/openedx/core/djangoapps/auth_exchange/tests/test_views.py @@ -168,11 +168,15 @@ def _verify_response(self, access_token, expected_status_code, token_type='Beare if expected_cookie_name: assert expected_cookie_name in response.cookies - def _create_dot_access_token(self, grant_type='Client credentials'): + def _create_dot_access_token(self, grant_type='Client credentials', skip_authorization=False): """ Create dot based access token """ - dot_application = dot_factories.ApplicationFactory(user=self.user, authorization_grant_type=grant_type) + dot_application = dot_factories.ApplicationFactory( + user=self.user, + authorization_grant_type=grant_type, + skip_authorization=skip_authorization, + ) return dot_factories.AccessTokenFactory(user=self.user, application=dot_application) def test_failure_with_invalid_token(self): @@ -189,6 +193,10 @@ def test_failure_with_dot_client_credentials_unsupported(self): access_token = self._create_dot_access_token() self._verify_response(access_token, expected_status_code=401) + def test_dot_client_credentials_supported_if_authorization_skipped(self): + access_token = self._create_dot_access_token(skip_authorization=True) + self._verify_response(access_token, expected_status_code=204, expected_cookie_name='sessionid') + def _create_jwt_token(self, grant_type='password', scope='email profile', use_asymmetric_key=True): """ Create jwt token diff --git a/openedx/core/djangoapps/auth_exchange/views.py b/openedx/core/djangoapps/auth_exchange/views.py index e4b302595277..60470aeba520 100644 --- a/openedx/core/djangoapps/auth_exchange/views.py +++ b/openedx/core/djangoapps/auth_exchange/views.py @@ -132,9 +132,10 @@ def _get_path_of_arbitrary_backend_for_user(user): return backend_path @staticmethod - def _ensure_access_token_has_password_grant(request): + def _ensure_access_token_has_password_grant_or_privileged_application(request): """ - Ensures the access token provided has password type grant. + Ensures the access token provided has password type grant, or if 'skip_authorization' + has been enabled, implying this is a trusted application. """ if is_jwt_authenticated(request): jwt_payload = get_decoded_jwt_from_auth(request) @@ -143,12 +144,17 @@ def _ensure_access_token_has_password_grant(request): else: token_query = dot_models.AccessToken.objects.select_related('user') dot_token = token_query.filter(token=request.auth).first() - if dot_token and dot_token.application.authorization_grant_type == dot_models.Application.GRANT_PASSWORD: + if dot_token and ( + dot_token.application.authorization_grant_type == dot_models.Application.GRANT_PASSWORD + or dot_token.application.skip_authorization + ): return raise AuthenticationFailed({ 'error_code': 'non_supported_token', - 'developer_message': 'Only access tokens with grant type password are supported.' + 'developer_message': 'Only Django Oauth Toolkit access tokens for applications which ' + 'are trusted (with "skip_authentication" set to True, or with grant type ' + 'password) are supported.' }) @staticmethod @@ -194,7 +200,7 @@ def post(self, request): request.user.backend = self._get_path_of_arbitrary_backend_for_user(request.user) self._ensure_user_is_not_disabled(request) - self._ensure_access_token_has_password_grant(request) + self._ensure_access_token_has_password_grant_or_privileged_application(request) self._ensure_jwt_is_asymmetric(request) login(request, request.user) # login generates and stores the user's cookies in the session