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 import http
77from django .contrib .auth .mixins import LoginRequiredMixin
88from django .contrib .auth .views import redirect_to_login
9- from django .http import HttpResponse
9+ from django .http import HttpResponse , HttpResponseBadRequest , HttpResponseRedirect
1010from django .shortcuts import resolve_url
11+ from django .urls import reverse
12+ from django .urls .exceptions import NoReverseMatch
1113from django .utils import timezone
1214from django .utils .decorators import method_decorator
1315from django .views .decorators .csrf import csrf_exempt
@@ -158,6 +160,8 @@ def get(self, request, *args, **kwargs):
158160 prompt = request .GET .get ("prompt" )
159161 if prompt == "login" :
160162 return self .handle_prompt_login ()
163+ elif prompt == "create" :
164+ return self .handle_prompt_create ()
161165
162166 all_scopes = get_scopes_backend ().get_all_scopes ()
163167 kwargs ["scopes_descriptions" ] = [all_scopes [scope ] for scope in scopes ]
@@ -256,13 +260,72 @@ def handle_prompt_login(self):
256260 self .get_redirect_field_name (),
257261 )
258262
263+ def handle_prompt_create (self ):
264+ """
265+ When prompt=create is in the authorization request,
266+ redirect the user to the registration page. After
267+ registration, the user should be redirected back to the
268+ authorization endpoint without the prompt parameter to
269+ continue the OIDC flow.
270+
271+ Implements OpenID Connect Prompt Create 1.0 specification.
272+ https://openid.net/specs/openid-connect-prompt-create-1_0.html
273+
274+ """
275+ try :
276+ assert not self .request .user .is_authenticated , "account_selection_required"
277+ path = self .request .build_absolute_uri ()
278+
279+ views_to_attempt = [oauth2_settings .OIDC_RP_INITIATED_REGISTRATION_VIEW_NAME , "account_signup" ]
280+
281+ registration_url = None
282+ for view_name in views_to_attempt :
283+ try :
284+ registration_url = reverse (view_name )
285+ continue
286+ except NoReverseMatch :
287+ pass
288+
289+ # Parse the current URL and remove the prompt parameter
290+ parsed = urlparse (path )
291+ parsed_query = dict (parse_qsl (parsed .query ))
292+ parsed_query .pop ("prompt" )
293+
294+ # Create the next parameter to redirect back to the authorization endpoint
295+ next_url = parsed ._replace (query = urlencode (parsed_query )).geturl ()
296+
297+ assert oauth2_settings .OIDC_RP_INITIATED_REGISTRATION_ENABLED , "access_denied"
298+ assert registration_url is not None , "access_denied"
299+
300+ # Add next parameter to registration URL
301+ separator = "&" if "?" in registration_url else "?"
302+ redirect_to = f"{ registration_url } { separator } next={ quote (next_url )} "
303+
304+ return HttpResponseRedirect (redirect_to )
305+
306+ except AssertionError as exc :
307+ redirect_uri = self .request .GET .get ("redirect_uri" )
308+ if redirect_uri :
309+ response_parameters = {"error" : str (exc )}
310+ state = self .request .GET .get ("state" )
311+ if state :
312+ response_parameters ["state" ] = state
313+
314+ separator = "&" if "?" in redirect_uri else "?"
315+ redirect_to = redirect_uri + separator + urlencode (response_parameters )
316+ return self .redirect (redirect_to , application = None )
317+ else :
318+ return HttpResponseBadRequest (str (exc ))
319+
259320 def handle_no_permission (self ):
260321 """
261322 Generate response for unauthorized users.
262323
263324 If prompt is set to none, then we redirect with an error code
264325 as defined by OIDC 3.1.2.6
265326
327+ If prompt is set to create, then we redirect to the registration page.
328+
266329 Some code copied from OAuthLibMixin.error_response, but that is designed
267330 to operated on OAuth1Error from oauthlib wrapped in a OAuthToolkitError
268331 """
@@ -280,6 +343,9 @@ def handle_no_permission(self):
280343 separator = "&" if "?" in redirect_uri else "?"
281344 redirect_to = redirect_uri + separator + urlencode (response_parameters )
282345 return self .redirect (redirect_to , application = None )
346+ elif prompt == "create" :
347+ # If prompt=create and user is not authenticated, redirect to registration
348+ return self .handle_prompt_create ()
283349 else :
284350 return super ().handle_no_permission ()
285351
0 commit comments