From a8283204c1f0d6fe693b436703c8ee81d1a74b44 Mon Sep 17 00:00:00 2001 From: Alexander Klimenko Date: Wed, 17 Jul 2019 11:45:54 +0300 Subject: [PATCH 1/2] Add non-HTTP schema logout redirect --- oidc_provider/lib/utils/common.py | 1 + .../tests/cases/test_end_session_endpoint.py | 29 +++++++++++++++---- oidc_provider/views.py | 8 +++++ 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/oidc_provider/lib/utils/common.py b/oidc_provider/lib/utils/common.py index 8d4623ec..f264b7ff 100644 --- a/oidc_provider/lib/utils/common.py +++ b/oidc_provider/lib/utils/common.py @@ -18,6 +18,7 @@ def redirect(uri): Custom Response object for redirecting to a Non-HTTP url scheme. """ response = HttpResponse('', status=302) + response.url = uri # TestCase.assertRedirects compatibility response['Location'] = uri return response diff --git a/oidc_provider/tests/cases/test_end_session_endpoint.py b/oidc_provider/tests/cases/test_end_session_endpoint.py index fb36f8e8..37e29ed8 100644 --- a/oidc_provider/tests/cases/test_end_session_endpoint.py +++ b/oidc_provider/tests/cases/test_end_session_endpoint.py @@ -28,15 +28,17 @@ def setUp(self): self.user = create_fake_user() self.oidc_client = create_fake_client('id_token') - self.LOGOUT_URL = 'http://example.com/logged-out/' - self.oidc_client.post_logout_redirect_uris = [self.LOGOUT_URL] + self.WEB_LOGOUT_URL = 'http://example.com/logged-out/' + self.APP_LOGOUT_URL = 'example-app://logged-out/' + self.oidc_client.post_logout_redirect_uris = [ + self.WEB_LOGOUT_URL, self.APP_LOGOUT_URL] self.oidc_client.save() self.url = reverse('oidc_provider:end-session') def test_redirects_when_aud_is_str(self): query_params = { - 'post_logout_redirect_uri': self.LOGOUT_URL, + 'post_logout_redirect_uri': self.WEB_LOGOUT_URL, } response = self.client.get(self.url, query_params) # With no id_token the OP MUST NOT redirect to the requested @@ -54,12 +56,12 @@ def test_redirects_when_aud_is_str(self): response = self.client.get(self.url, query_params) self.assertRedirects( - response, self.LOGOUT_URL, fetch_redirect_response=False) + response, self.WEB_LOGOUT_URL, fetch_redirect_response=False) def test_redirects_when_aud_is_list(self): """Check with 'aud' containing a list of str.""" query_params = { - 'post_logout_redirect_uri': self.LOGOUT_URL, + 'post_logout_redirect_uri': self.WEB_LOGOUT_URL, } token = create_token(self.user, self.oidc_client, []) id_token_dic = create_id_token( @@ -69,7 +71,7 @@ def test_redirects_when_aud_is_list(self): query_params['id_token_hint'] = id_token response = self.client.get(self.url, query_params) self.assertRedirects( - response, self.LOGOUT_URL, fetch_redirect_response=False) + response, self.WEB_LOGOUT_URL, fetch_redirect_response=False) @mock.patch(settings.get('OIDC_AFTER_END_SESSION_HOOK')) def test_call_post_end_session_hook(self, hook_function): @@ -78,3 +80,18 @@ def test_call_post_end_session_hook(self, hook_function): self.assertTrue( hook_function.call_count == 1, 'OIDC_AFTER_END_SESSION_HOOK should be called once') + + def test_redirects_to_app(self): + query_params = { + 'post_logout_redirect_uri': self.APP_LOGOUT_URL, + } + token = create_token(self.user, self.oidc_client, []) + id_token_dic = create_id_token( + token=token, user=self.user, aud=self.oidc_client.client_id) + id_token_dic['aud'] = [id_token_dic['aud']] + id_token = encode_id_token(id_token_dic, self.oidc_client) + query_params['id_token_hint'] = id_token + response = self.client.get(self.url, query_params, + fetch_redirect_response=False) + self.assertRedirects( + response, self.APP_LOGOUT_URL, fetch_redirect_response=False) diff --git a/oidc_provider/views.py b/oidc_provider/views.py index def720f5..1f59f29e 100644 --- a/oidc_provider/views.py +++ b/oidc_provider/views.py @@ -26,6 +26,7 @@ from django.views.decorators.clickjacking import xframe_options_exempt from django.views.decorators.http import require_http_methods from django.views.generic import View +from django.contrib.auth import logout as auth_logout from jwkest import long_to_base64 from oidc_provider.compat import get_attr_or_callable @@ -349,6 +350,13 @@ def dispatch(self, request, *args, **kwargs): ) self.next_page = next_page + + # Rewrite LogoutView.dispatch to allowing Non-HTTP url scheme redirect + auth_logout(request) + next_page = self.get_next_page() + if next_page: + return redirect(next_page) + return super(EndSessionView, self).dispatch(request, *args, **kwargs) From f65f54dad8fcc0928e10c3c44bc2723e317b3b00 Mon Sep 17 00:00:00 2001 From: Alexander Klimenko Date: Wed, 17 Jul 2019 12:57:53 +0300 Subject: [PATCH 2/2] Add oauth2.py flake8 fix --- oidc_provider/lib/utils/oauth2.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/oidc_provider/lib/utils/oauth2.py b/oidc_provider/lib/utils/oauth2.py index fcdd68a3..a3fe7a09 100644 --- a/oidc_provider/lib/utils/oauth2.py +++ b/oidc_provider/lib/utils/oauth2.py @@ -21,7 +21,7 @@ def extract_access_token(request): """ auth_header = request.META.get('HTTP_AUTHORIZATION', '') - if re.compile('^[Bb]earer\s{1}.+$').match(auth_header): + if re.compile(r'^[Bb]earer\s{1}.+$').match(auth_header): access_token = auth_header.split()[1] else: access_token = request.GET.get('access_token', '') @@ -39,7 +39,7 @@ def extract_client_auth(request): """ auth_header = request.META.get('HTTP_AUTHORIZATION', '') - if re.compile('^Basic\s{1}.+$').match(auth_header): + if re.compile(r'^Basic\s{1}.+$').match(auth_header): b64_user_pass = auth_header.split()[1] try: user_pass = b64decode(b64_user_pass).decode('utf-8').split(':')