Skip to content

Commit cbf8dcf

Browse files
whytheplatypusjleclanche
authored andcommitted
Return state with authorization denied error
Conforms to RFC6749 section 4.1.2.1: https://tools.ietf.org/html/rfc6749#section-4.1.2.1 REQUIRED if a "state" parameter was present in the client authorization request. The exact value received from the client. (cherry picked from commit be659ca)
1 parent 7352e35 commit cbf8dcf

File tree

2 files changed

+28
-2
lines changed

2 files changed

+28
-2
lines changed

oauth2_provider/oauth2_backends.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,8 @@ def create_authorization_response(self, request, scopes, credentials, allow):
110110
"""
111111
try:
112112
if not allow:
113-
raise oauth2.AccessDeniedError()
113+
raise oauth2.AccessDeniedError(
114+
state=credentials.get("state", None))
114115

115116
# add current user to credentials. this will be used by OAUTH2_VALIDATOR_CLASS
116117
credentials["user"] = request.user

tests/test_authorization_code.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,26 @@ def test_code_post_auth_deny(self):
335335
response = self.client.post(reverse("oauth2_provider:authorize"), data=form_data)
336336
self.assertEqual(response.status_code, 302)
337337
self.assertIn("error=access_denied", response["Location"])
338+
self.assertIn("state=random_state_string", response["Location"])
339+
340+
def test_code_post_auth_deny_no_state(self):
341+
"""
342+
Test optional state when resource owner deny access
343+
"""
344+
self.client.login(username="test_user", password="123456")
345+
346+
form_data = {
347+
"client_id": self.application.client_id,
348+
"scope": "read write",
349+
"redirect_uri": "http://example.org",
350+
"response_type": "code",
351+
"allow": False,
352+
}
353+
354+
response = self.client.post(reverse("oauth2_provider:authorize"), data=form_data)
355+
self.assertEqual(response.status_code, 302)
356+
self.assertIn("error=access_denied", response["Location"])
357+
self.assertNotIn("state", response["Location"])
338358

339359
def test_code_post_auth_bad_responsetype(self):
340360
"""
@@ -433,6 +453,7 @@ def test_code_post_auth_deny_custom_redirect_uri_scheme(self):
433453
self.assertEqual(response.status_code, 302)
434454
self.assertIn("custom-scheme://example.com?", response["Location"])
435455
self.assertIn("error=access_denied", response["Location"])
456+
self.assertIn("state=random_state_string", response["Location"])
436457

437458
def test_code_post_auth_redirection_uri_with_querystring(self):
438459
"""
@@ -455,6 +476,7 @@ def test_code_post_auth_redirection_uri_with_querystring(self):
455476
self.assertEqual(response.status_code, 302)
456477
self.assertIn("http://example.com?foo=bar", response["Location"])
457478
self.assertIn("code=", response["Location"])
479+
self.assertIn("state=random_state_string", response["Location"])
458480

459481
def test_code_post_auth_failing_redirection_uri_with_querystring(self):
460482
"""
@@ -475,7 +497,10 @@ def test_code_post_auth_failing_redirection_uri_with_querystring(self):
475497

476498
response = self.client.post(reverse("oauth2_provider:authorize"), data=form_data)
477499
self.assertEqual(response.status_code, 302)
478-
self.assertEqual("http://example.com?foo=bar&error=access_denied", response["Location"])
500+
self.assertIn("http://example.com?", response["Location"])
501+
self.assertIn("error=access_denied", response["Location"])
502+
self.assertIn("state=random_state_string", response["Location"])
503+
self.assertIn("foo=bar", response["Location"])
479504

480505
def test_code_post_auth_fails_when_redirect_uri_path_is_invalid(self):
481506
"""

0 commit comments

Comments
 (0)