Skip to content

Commit 5e48ee8

Browse files
andyzicklerdopry
authored andcommitted
Add prompt=none support (#1268)
1 parent 4f59b06 commit 5e48ee8

File tree

4 files changed

+56
-0
lines changed

4 files changed

+56
-0
lines changed

AUTHORS

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Allisson Azevedo
2222
Andrea Greco
2323
Andrej Zbín
2424
Andrew Chen Wang
25+
Andrew Zickler
2526
Antoine Laurent
2627
Anvesh Agarwal
2728
Aristóbulo Meneses

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2626
* #1311 Add option to disable client_secret hashing to allow verifying JWTs' signatures.
2727
* #1337 Gracefully handle expired or deleted refresh tokens, in `validate_user`.
2828
* #1350 Support Python 3.12 and Django 5.0
29+
* #1268 Support prompt=none in authentication request for Silent Authentication
2930

3031
### Fixed
3132
* #1322 Instructions in documentation on how to create a code challenge and code verifier

oauth2_provider/views/base.py

+27
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,33 @@ def handle_prompt_login(self):
244244
self.get_redirect_field_name(),
245245
)
246246

247+
def handle_no_permission(self):
248+
"""
249+
Generate response for unauthorized users.
250+
251+
If prompt is set to none, then we redirect with an error code
252+
as defined by OIDC 3.1.2.6
253+
254+
Some code copied from OAuthLibMixin.error_response, but that is designed
255+
to operated on OAuth1Error from oauthlib wrapped in a OAuthToolkitError
256+
"""
257+
prompt = self.request.GET.get("prompt")
258+
redirect_uri = self.request.GET.get("redirect_uri")
259+
if prompt == "none" and redirect_uri:
260+
response_parameters = {"error": "login_required"}
261+
262+
# REQUIRED if the Authorization Request included the state parameter.
263+
# Set to the value received from the Client
264+
state = self.request.GET.get("state")
265+
if state:
266+
response_parameters["state"] = state
267+
268+
separator = "&" if "?" in redirect_uri else "?"
269+
redirect_to = redirect_uri + separator + urlencode(response_parameters)
270+
return self.redirect(redirect_to, application=None)
271+
else:
272+
super().handle_no_permission()
273+
247274

248275
@method_decorator(csrf_exempt, name="dispatch")
249276
class TokenView(OAuthLibMixin, View):

tests/test_authorization_code.py

+27
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,33 @@ def test_prompt_login(self):
645645

646646
self.assertNotIn("prompt=login", next)
647647

648+
def test_prompt_none_unauthorized(self):
649+
"""
650+
Test response for redirect when supplied with prompt: none
651+
652+
Should redirect to redirect_uri with an error of login_required
653+
"""
654+
self.oauth2_settings.PKCE_REQUIRED = False
655+
656+
query_data = {
657+
"client_id": self.application.client_id,
658+
"response_type": "code",
659+
"state": "random_state_string",
660+
"scope": "read write",
661+
"redirect_uri": "http://example.org",
662+
"prompt": "none",
663+
}
664+
665+
response = self.client.get(reverse("oauth2_provider:authorize"), data=query_data)
666+
667+
self.assertEqual(response.status_code, 302)
668+
669+
scheme, netloc, path, params, query, fragment = urlparse(response["Location"])
670+
parsed_query = parse_qs(query)
671+
672+
self.assertIn("login_required", parsed_query["error"])
673+
self.assertIn("random_state_string", parsed_query["state"])
674+
648675

649676
class BaseAuthorizationCodeTokenView(BaseTest):
650677
def get_auth(self, scope="read write"):

0 commit comments

Comments
 (0)