Skip to content

Commit 8b29c30

Browse files
committed
Work on end_session_endpoint
1 parent 102f1c1 commit 8b29c30

File tree

4 files changed

+326
-211
lines changed

4 files changed

+326
-211
lines changed

.vscode/settings.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"[python]": {
3+
"editor.codeActionsOnSave": {
4+
"source.organizeImports": true
5+
}
6+
},
7+
"python.formatting.provider": "black",
8+
"editor.formatOnSave": true,
9+
"black-formatter.args": [
10+
"--line-length=100",
11+
"--preview",
12+
],
13+
"isort.args": [
14+
"--profile",
15+
"black"
16+
],
17+
}

oidc_provider/tests/cases/test_end_session_endpoint.py

Lines changed: 99 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from urllib.parse import urlencode
55

66
from django.core.management import call_command
7+
78
try:
89
from django.urls import reverse
910
except ImportError:
@@ -15,7 +16,6 @@
1516
create_id_token,
1617
encode_id_token,
1718
)
18-
from oidc_provider import settings
1919
from oidc_provider.tests.app.utils import (
2020
create_fake_client,
2121
create_fake_user,
@@ -29,81 +29,103 @@ class EndSessionTestCase(TestCase):
2929
"""
3030

3131
def setUp(self):
32-
call_command('creatersakey')
32+
call_command("creatersakey")
3333
self.user = create_fake_user()
3434
self.client.force_login(self.user)
3535

3636
# Create a client with a custom logout URL.
37-
self.oidc_client = create_fake_client('id_token')
38-
self.url_logout = 'http://example.com/logged-out/'
37+
self.oidc_client = create_fake_client("id_token")
38+
self.url_logout = "http://example.com/logged-out/"
3939
self.oidc_client.post_logout_redirect_uris = [self.url_logout]
4040
self.oidc_client.save()
4141

4242
# Create a valid ID Token for the user.
4343
token = create_token(self.user, self.oidc_client, [])
44-
id_token_dic = create_id_token(
45-
token=token, user=self.user, aud=self.oidc_client.client_id)
44+
id_token_dic = create_id_token(token=token, user=self.user, aud=self.oidc_client.client_id)
4645
self.id_token = encode_id_token(id_token_dic, self.oidc_client)
4746

48-
self.url = reverse('oidc_provider:end-session')
49-
self.url_prompt = reverse('oidc_provider:end-session-prompt')
47+
self.url = reverse("oidc_provider:end-session")
48+
self.url_prompt = reverse("oidc_provider:end-session-prompt")
5049

5150
def test_id_token_hint_not_present_user_prompted(self):
5251
response = self.client.get(self.url)
5352
# We should display a logout consent prompt if id_token_hint parameter is not present.
5453
self.assertEqual(response.status_code, 302)
55-
self.assertEqual(response.headers['Location'], self.url_prompt)
56-
57-
def test_id_token_hint_is_present_user_redirected_to_client_logout_url(self):
54+
self.assertEqual(response.headers["Location"], self.url_prompt)
55+
# User still logged in.
56+
self.assertIn("_auth_user_id", self.client.session)
57+
58+
@mock.patch("oidc_provider.views.after_end_session_hook")
59+
def test_id_token_hint_is_present_user_redirected_to_client_logout_url(
60+
self, after_end_session_hook
61+
):
5862
query_params = {
59-
'id_token_hint': self.id_token,
63+
"id_token_hint": self.id_token,
6064
}
6165
response = self.client.get(self.url, query_params)
62-
# ID Token is valid so user was
66+
# ID Token is valid so user was redirected to registered uri.
6367
self.assertEqual(response.status_code, 302)
64-
self.assertEqual(response.headers['Location'], self.url_logout)
65-
66-
def test_id_token_hint_is_present_user_redirected_to_client_logout_url_with_post(self):
68+
self.assertEqual(response.headers["Location"], self.url_logout)
69+
# User logged out.
70+
self.assertNotIn("_auth_user_id", self.client.session)
71+
# End session hook should be called.
72+
self.assertTrue(after_end_session_hook.called)
73+
self.assertTrue(after_end_session_hook.call_count == 1)
74+
75+
@mock.patch("oidc_provider.views.after_end_session_hook")
76+
def test_id_token_hint_is_present_user_redirected_to_client_logout_url_with_post(
77+
self, after_end_session_hook
78+
):
6779
data = {
68-
'id_token_hint': self.id_token,
80+
"id_token_hint": self.id_token,
6981
}
7082
response = self.client.post(self.url, data)
7183
# ID Token is valid so user was
7284
self.assertEqual(response.status_code, 302)
73-
self.assertEqual(response.headers['Location'], self.url_logout)
85+
self.assertEqual(response.headers["Location"], self.url_logout)
86+
# User logged out.
87+
self.assertNotIn("_auth_user_id", self.client.session)
88+
# End session hook should be called.
89+
self.assertTrue(after_end_session_hook.called)
90+
self.assertTrue(after_end_session_hook.call_count == 1)
7491

7592
def test_state_is_present_and_being_passed_to_logout_url(self):
7693
query_params = {
77-
'id_token_hint': self.id_token,
78-
'state': 'ABCDE',
94+
"id_token_hint": self.id_token,
95+
"state": "ABCDE",
7996
}
8097
response = self.client.get(self.url, query_params)
8198
# Let's ensure state is being passed to the logout url.
8299
self.assertEqual(response.status_code, 302)
83-
self.assertEqual(response.headers['Location'], '{0}?state={1}'.format(self.url_logout, 'ABCDE'))
84-
100+
self.assertEqual(
101+
response.headers["Location"], "{0}?state={1}".format(self.url_logout, "ABCDE")
102+
)
103+
85104
def test_post_logout_uri_not_in_client_urls(self):
86105
query_params = {
87-
'id_token_hint': self.id_token,
88-
'post_logout_redirect_uri': 'http://other.com/bye/',
106+
"id_token_hint": self.id_token,
107+
"post_logout_redirect_uri": "http://other.com/bye/",
89108
}
90109
response = self.client.get(self.url, query_params)
91110
# We prompt the user since the post logout url is not from client urls.
92111
# Also ensure client_id is present since we could validate id_token_hint.
93112
self.assertEqual(response.status_code, 302)
94-
self.assertEqual(response.headers['Location'], '{0}?client_id={1}'.format(self.url_prompt, self.oidc_client.client_id))
113+
self.assertEqual(
114+
response.headers["Location"],
115+
"{0}?client_id={1}".format(self.url_prompt, self.oidc_client.client_id),
116+
)
95117

96118
def test_prompt_view_redirecting_to_client_post_logout_since_user_unauthenticated(self):
97119
self.client.logout()
98120
query_params = {
99-
'client_id': self.oidc_client.client_id,
121+
"client_id": self.oidc_client.client_id,
100122
}
101123
response = self.client.get(self.url_prompt, query_params)
102124
# Since user is unauthenticated on the backend, we send it back to client post logout
103125
# registered uri.
104126
self.assertEqual(response.status_code, 302)
105-
self.assertEqual(response.headers['Location'], self.url_logout)
106-
127+
self.assertEqual(response.headers["Location"], self.url_logout)
128+
107129
def test_prompt_view_raising_404_since_user_unauthenticated_and_no_client(self):
108130
self.client.logout()
109131
response = self.client.get(self.url_prompt)
@@ -113,57 +135,81 @@ def test_prompt_view_raising_404_since_user_unauthenticated_and_no_client(self):
113135

114136
def test_prompt_view_displaying_logout_decision_form_to_user(self):
115137
query_params = {
116-
'client_id': self.oidc_client.client_id,
138+
"client_id": self.oidc_client.client_id,
117139
}
118140
response = self.client.get(self.url_prompt, query_params)
119141
# User is prompted to logout with client information displayed.
120-
self.assertContains(response, '<p>Hi <strong>johndoe@example.com</strong>, are you sure you want to log out from <strong>Some Client</strong> app?</p>', status_code=200, html=True)
142+
self.assertContains(
143+
response,
144+
"<p>Hi <strong>johndoe@example.com</strong>, are you sure you want to log out from <strong>Some Client</strong> app?</p>", # noqa
145+
status_code=200,
146+
html=True,
147+
)
121148

122149
def test_prompt_view_displaying_logout_decision_form_to_user_no_client(self):
123150
response = self.client.get(self.url_prompt)
124151
# User is prompted to logout without client information displayed.
125-
self.assertContains(response, '<p>Hi <strong>johndoe@example.com</strong>, are you sure you want to log out?</p>', status_code=200, html=True)
126-
127-
@mock.patch('oidc_provider.views.after_end_session_hook')
152+
self.assertContains(
153+
response,
154+
"<p>Hi <strong>johndoe@example.com</strong>, are you sure you want to log out?</p>",
155+
status_code=200,
156+
html=True,
157+
)
158+
159+
@mock.patch("oidc_provider.views.after_end_session_hook")
128160
def test_prompt_view_user_logged_out_after_form_allowed(self, after_end_session_hook):
129-
self.assertIn('_auth_user_id', self.client.session)
161+
self.assertIn("_auth_user_id", self.client.session)
130162
# We want to POST to /end-session-prompt/?client_id=ABC endpoint.
131-
url_prompt_with_client = self.url_prompt + '?' + urlencode({
132-
'client_id': self.oidc_client.client_id,
133-
})
163+
url_prompt_with_client = (
164+
self.url_prompt
165+
+ "?"
166+
+ urlencode(
167+
{
168+
"client_id": self.oidc_client.client_id,
169+
}
170+
)
171+
)
134172
data = {
135-
'allow': 'Anything', # This means user allowed being logged out.
173+
"allow": "Anything", # This means user allowed being logged out.
136174
}
137175
response = self.client.post(url_prompt_with_client, data)
138176
# Ensure user is now logged out and redirected to client post logout uri.
139-
self.assertNotIn('_auth_user_id', self.client.session)
177+
self.assertNotIn("_auth_user_id", self.client.session)
140178
self.assertEqual(response.status_code, 302)
141-
self.assertEqual(response.headers['Location'], self.url_logout)
179+
self.assertEqual(response.headers["Location"], self.url_logout)
142180
# End session hook should be called.
143181
self.assertTrue(after_end_session_hook.called)
144182
self.assertTrue(after_end_session_hook.call_count == 1)
145-
146-
@mock.patch('oidc_provider.views.after_end_session_hook')
183+
184+
@mock.patch("oidc_provider.views.after_end_session_hook")
147185
def test_prompt_view_user_logged_out_after_form_not_allowed(self, after_end_session_hook):
148-
self.assertIn('_auth_user_id', self.client.session)
186+
self.assertIn("_auth_user_id", self.client.session)
149187
# We want to POST to /end-session-prompt/?client_id=ABC endpoint.
150-
url_prompt_with_client = self.url_prompt + '?' + urlencode({
151-
'client_id': self.oidc_client.client_id,
152-
})
188+
url_prompt_with_client = (
189+
self.url_prompt
190+
+ "?"
191+
+ urlencode(
192+
{
193+
"client_id": self.oidc_client.client_id,
194+
}
195+
)
196+
)
153197
response = self.client.post(url_prompt_with_client) # No data.
154198
# Ensure user is still logged in and redirected to client post logout uri.
155-
self.assertIn('_auth_user_id', self.client.session)
199+
self.assertIn("_auth_user_id", self.client.session)
156200
self.assertEqual(response.status_code, 302)
157-
self.assertEqual(response.headers['Location'], self.url_logout)
201+
self.assertEqual(response.headers["Location"], self.url_logout)
158202
# End session hook should not be called.
159203
self.assertFalse(after_end_session_hook.called)
160-
161-
@mock.patch('oidc_provider.views.after_end_session_hook')
162-
def test_prompt_view_user_not_logged_out_after_form_not_allowed_no_client(self, after_end_session_hook):
163-
self.assertIn('_auth_user_id', self.client.session)
204+
205+
@mock.patch("oidc_provider.views.after_end_session_hook")
206+
def test_prompt_view_user_not_logged_out_after_form_not_allowed_no_client(
207+
self, after_end_session_hook
208+
):
209+
self.assertIn("_auth_user_id", self.client.session)
164210
response = self.client.post(self.url_prompt) # No data.
165211
# Ensure user is still logged in and 404 NOT FOUND was raised.
166-
self.assertIn('_auth_user_id', self.client.session)
212+
self.assertIn("_auth_user_id", self.client.session)
167213
self.assertEqual(response.status_code, 404)
168214
# End session hook should not be called.
169215
self.assertFalse(after_end_session_hook.called)

oidc_provider/urls.py

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,29 @@
66
views,
77
)
88

9-
app_name = 'oidc_provider'
9+
app_name = "oidc_provider"
1010
urlpatterns = [
11-
re_path(r'^authorize/?$', views.AuthorizeView.as_view(), name='authorize'),
12-
re_path(r'^token/?$', csrf_exempt(views.TokenView.as_view()), name='token'),
13-
re_path(r'^userinfo/?$', csrf_exempt(views.userinfo), name='userinfo'),
14-
re_path(r'^end-session/?$', views.EndSessionView.as_view(), name='end-session'),
15-
re_path(r'^end-session-prompt/?$', views.EndSessionPromptView.as_view(), name='end-session-prompt'),
16-
re_path(r'^\.well-known/openid-configuration/?$', views.ProviderInfoView.as_view(),
17-
name='provider-info'),
18-
re_path(r'^introspect/?$', views.TokenIntrospectionView.as_view(), name='token-introspection'),
19-
re_path(r'^jwks/?$', views.JwksView.as_view(), name='jwks'),
11+
re_path(r"^authorize/?$", views.AuthorizeView.as_view(), name="authorize"),
12+
re_path(r"^token/?$", csrf_exempt(views.TokenView.as_view()), name="token"),
13+
re_path(r"^userinfo/?$", csrf_exempt(views.userinfo), name="userinfo"),
14+
re_path(r"^end-session/?$", views.EndSessionView.as_view(), name="end-session"),
15+
re_path(
16+
r"^end-session-prompt/?$", views.EndSessionPromptView.as_view(), name="end-session-prompt"
17+
),
18+
re_path(
19+
r"^\.well-known/openid-configuration/?$",
20+
views.ProviderInfoView.as_view(),
21+
name="provider-info",
22+
),
23+
re_path(r"^introspect/?$", views.TokenIntrospectionView.as_view(), name="token-introspection"),
24+
re_path(r"^jwks/?$", views.JwksView.as_view(), name="jwks"),
2025
]
2126

22-
if settings.get('OIDC_SESSION_MANAGEMENT_ENABLE'):
27+
if settings.get("OIDC_SESSION_MANAGEMENT_ENABLE"):
2328
urlpatterns += [
24-
re_path(r'^check-session-iframe/?$', views.CheckSessionIframeView.as_view(),
25-
name='check-session-iframe'),
29+
re_path(
30+
r"^check-session-iframe/?$",
31+
views.CheckSessionIframeView.as_view(),
32+
name="check-session-iframe",
33+
),
2634
]

0 commit comments

Comments
 (0)