11import hashlib
22import json
33import logging
4- from urllib .parse import parse_qsl , urlencode , urlparse
4+ from urllib .parse import parse_qsl , quote , urlencode , urlparse
55
66from django .contrib .auth .mixins import LoginRequiredMixin
77from django .contrib .auth .views import redirect_to_login
8- from django .http import HttpResponse
8+ from django .http import HttpResponse , HttpResponseBadRequest , HttpResponseRedirect
99from django .shortcuts import resolve_url
10+ from django .urls import reverse
11+ from django .urls .exceptions import NoReverseMatch
1012from django .utils import timezone
1113from django .utils .decorators import method_decorator
1214from django .views .decorators .csrf import csrf_exempt
@@ -154,6 +156,8 @@ def get(self, request, *args, **kwargs):
154156 prompt = request .GET .get ("prompt" )
155157 if prompt == "login" :
156158 return self .handle_prompt_login ()
159+ elif prompt == "create" :
160+ return self .handle_prompt_create ()
157161
158162 all_scopes = get_scopes_backend ().get_all_scopes ()
159163 kwargs ["scopes_descriptions" ] = [all_scopes [scope ] for scope in scopes ]
@@ -252,13 +256,72 @@ def handle_prompt_login(self):
252256 self .get_redirect_field_name (),
253257 )
254258
259+ def handle_prompt_create (self ):
260+ """
261+ When prompt=create is in the authorization request,
262+ redirect the user to the registration page. After
263+ registration, the user should be redirected back to the
264+ authorization endpoint without the prompt parameter to
265+ continue the OIDC flow.
266+
267+ Implements OpenID Connect Prompt Create 1.0 specification.
268+ https://openid.net/specs/openid-connect-prompt-create-1_0.html
269+
270+ """
271+ try :
272+ assert not self .request .user .is_authenticated , "account_selection_required"
273+ path = self .request .build_absolute_uri ()
274+
275+ views_to_attempt = [oauth2_settings .OIDC_RP_INITIATED_REGISTRATION_VIEW_NAME , "account_signup" ]
276+
277+ registration_url = None
278+ for view_name in views_to_attempt :
279+ try :
280+ registration_url = reverse (view_name )
281+ continue
282+ except NoReverseMatch :
283+ pass
284+
285+ # Parse the current URL and remove the prompt parameter
286+ parsed = urlparse (path )
287+ parsed_query = dict (parse_qsl (parsed .query ))
288+ parsed_query .pop ("prompt" )
289+
290+ # Create the next parameter to redirect back to the authorization endpoint
291+ next_url = parsed ._replace (query = urlencode (parsed_query )).geturl ()
292+
293+ assert oauth2_settings .OIDC_RP_INITIATED_REGISTRATION_ENABLED , "access_denied"
294+ assert registration_url is not None , "access_denied"
295+
296+ # Add next parameter to registration URL
297+ separator = "&" if "?" in registration_url else "?"
298+ redirect_to = f"{ registration_url } { separator } next={ quote (next_url )} "
299+
300+ return HttpResponseRedirect (redirect_to )
301+
302+ except AssertionError as exc :
303+ redirect_uri = self .request .GET .get ("redirect_uri" )
304+ if redirect_uri :
305+ response_parameters = {"error" : str (exc )}
306+ state = self .request .GET .get ("state" )
307+ if state :
308+ response_parameters ["state" ] = state
309+
310+ separator = "&" if "?" in redirect_uri else "?"
311+ redirect_to = redirect_uri + separator + urlencode (response_parameters )
312+ return self .redirect (redirect_to , application = None )
313+ else :
314+ return HttpResponseBadRequest (str (exc ))
315+
255316 def handle_no_permission (self ):
256317 """
257318 Generate response for unauthorized users.
258319
259320 If prompt is set to none, then we redirect with an error code
260321 as defined by OIDC 3.1.2.6
261322
323+ If prompt is set to create, then we redirect to the registration page.
324+
262325 Some code copied from OAuthLibMixin.error_response, but that is designed
263326 to operated on OAuth1Error from oauthlib wrapped in a OAuthToolkitError
264327 """
@@ -276,6 +339,9 @@ def handle_no_permission(self):
276339 separator = "&" if "?" in redirect_uri else "?"
277340 redirect_to = redirect_uri + separator + urlencode (response_parameters )
278341 return self .redirect (redirect_to , application = None )
342+ elif prompt == "create" :
343+ # If prompt=create and user is not authenticated, redirect to registration
344+ return self .handle_prompt_create ()
279345 else :
280346 return super ().handle_no_permission ()
281347
0 commit comments