1
1
import hashlib
2
2
import json
3
3
import logging
4
- from urllib .parse import parse_qsl , urlencode , urlparse
4
+ from urllib .parse import parse_qsl , quote , urlencode , urlparse
5
5
6
6
from django .contrib .auth .mixins import LoginRequiredMixin
7
7
from django .contrib .auth .views import redirect_to_login
8
- from django .http import HttpResponse
8
+ from django .http import HttpResponse , HttpResponseBadRequest , HttpResponseRedirect
9
9
from django .shortcuts import resolve_url
10
+ from django .urls import reverse
11
+ from django .urls .exceptions import NoReverseMatch
10
12
from django .utils import timezone
11
13
from django .utils .decorators import method_decorator
12
14
from django .views .decorators .csrf import csrf_exempt
@@ -154,6 +156,8 @@ def get(self, request, *args, **kwargs):
154
156
prompt = request .GET .get ("prompt" )
155
157
if prompt == "login" :
156
158
return self .handle_prompt_login ()
159
+ elif prompt == "create" :
160
+ return self .handle_prompt_create ()
157
161
158
162
all_scopes = get_scopes_backend ().get_all_scopes ()
159
163
kwargs ["scopes_descriptions" ] = [all_scopes [scope ] for scope in scopes ]
@@ -252,13 +256,72 @@ def handle_prompt_login(self):
252
256
self .get_redirect_field_name (),
253
257
)
254
258
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
+
255
316
def handle_no_permission (self ):
256
317
"""
257
318
Generate response for unauthorized users.
258
319
259
320
If prompt is set to none, then we redirect with an error code
260
321
as defined by OIDC 3.1.2.6
261
322
323
+ If prompt is set to create, then we redirect to the registration page.
324
+
262
325
Some code copied from OAuthLibMixin.error_response, but that is designed
263
326
to operated on OAuth1Error from oauthlib wrapped in a OAuthToolkitError
264
327
"""
@@ -276,6 +339,9 @@ def handle_no_permission(self):
276
339
separator = "&" if "?" in redirect_uri else "?"
277
340
redirect_to = redirect_uri + separator + urlencode (response_parameters )
278
341
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 ()
279
345
else :
280
346
return super ().handle_no_permission ()
281
347
0 commit comments