@@ -444,14 +444,16 @@ async def complete_sso_login_request(
444444 if not user_id :
445445 attributes = await self ._call_attribute_mapper (sso_to_matrix_id_mapper )
446446
447- if attributes .localpart is None :
448- # the mapper doesn't return a username. bail out with a redirect to
449- # the username picker.
450- await self ._redirect_to_username_picker (
447+ next_step_url = self ._get_url_for_next_new_user_step (
448+ attributes = attributes
449+ )
450+ if next_step_url :
451+ await self ._redirect_to_next_new_user_step (
451452 auth_provider_id ,
452453 remote_user_id ,
453454 attributes ,
454455 client_redirect_url ,
456+ next_step_url ,
455457 extra_login_attributes ,
456458 )
457459
@@ -530,18 +532,53 @@ async def _call_attribute_mapper(
530532 )
531533 return attributes
532534
533- async def _redirect_to_username_picker (
535+ def _get_url_for_next_new_user_step (
536+ self ,
537+ attributes : Optional [UserAttributes ] = None ,
538+ session : Optional [UsernameMappingSession ] = None ,
539+ ) -> bytes :
540+ """Returns the URL to redirect to for the next step of new user registration
541+
542+ Given attributes from the user mapping provider or a UsernameMappingSession,
543+ returns the URL to redirect to for the next step of the registration flow.
544+
545+ Args:
546+ attributes: the user attributes returned by the user mapping provider,
547+ from before a UsernameMappingSession has begun.
548+
549+ session: an active UsernameMappingSession, possibly with some of its
550+ attributes chosen by the user.
551+
552+ Returns:
553+ The URL to redirect to, or an empty value if no redirect is necessary
554+ """
555+ # Must provide either attributes or session, not both
556+ assert (attributes is not None ) != (session is not None )
557+
558+ if (attributes and attributes .localpart is None ) or (
559+ session and session .chosen_localpart is None
560+ ):
561+ return b"/_synapse/client/pick_username/account_details"
562+ elif self ._consent_at_registration and not (
563+ session and session .terms_accepted_version
564+ ):
565+ return b"/_synapse/client/new_user_consent"
566+ else :
567+ return b"/_synapse/client/sso_register" if session else b""
568+
569+ async def _redirect_to_next_new_user_step (
534570 self ,
535571 auth_provider_id : str ,
536572 remote_user_id : str ,
537573 attributes : UserAttributes ,
538574 client_redirect_url : str ,
575+ next_step_url : bytes ,
539576 extra_login_attributes : Optional [JsonDict ],
540577 ) -> NoReturn :
541578 """Creates a UsernameMappingSession and redirects the browser
542579
543- Called if the user mapping provider doesn't return a localpart for a new user.
544- Raises a RedirectException which redirects the browser to the username picker .
580+ Called if the user mapping provider doesn't return complete information for a new user.
581+ Raises a RedirectException which redirects the browser to a specified URL .
545582
546583 Args:
547584 auth_provider_id: A unique identifier for this SSO provider, e.g.
@@ -554,12 +591,15 @@ async def _redirect_to_username_picker(
554591 client_redirect_url: The redirect URL passed in by the client, which we
555592 will eventually redirect back to.
556593
594+ next_step_url: The URL to redirect to for the next step of the new user flow.
595+
557596 extra_login_attributes: An optional dictionary of extra
558597 attributes to be provided to the client in the login response.
559598
560599 Raises:
561600 RedirectException
562601 """
602+ # TODO: If needed, allow using/looking up an existing session here.
563603 session_id = random_string (16 )
564604 now = self ._clock .time_msec ()
565605 session = UsernameMappingSession (
@@ -570,13 +610,18 @@ async def _redirect_to_username_picker(
570610 client_redirect_url = client_redirect_url ,
571611 expiry_time_ms = now + self ._MAPPING_SESSION_VALIDITY_PERIOD_MS ,
572612 extra_login_attributes = extra_login_attributes ,
613+ # Treat the localpart returned by the user mapping provider as though
614+ # it was chosen by the user. If it's None, it must be chosen eventually.
615+ chosen_localpart = attributes .localpart ,
616+ # TODO: Consider letting the user mapping provider specify defaults for
617+ # other user-chosen attributes.
573618 )
574619
575620 self ._username_mapping_sessions [session_id ] = session
576621 logger .info ("Recorded registration session id %s" , session_id )
577622
578- # Set the cookie and redirect to the username picker
579- e = RedirectException (b"/_synapse/client/pick_username/account_details" )
623+ # Set the cookie and redirect to the next step
624+ e = RedirectException (next_step_url )
580625 e .cookies .append (
581626 b"%s=%s; path=/"
582627 % (USERNAME_MAPPING_SESSION_COOKIE_NAME , session_id .encode ("ascii" ))
@@ -805,16 +850,9 @@ async def handle_submit_username_request(
805850 )
806851 session .emails_to_use = filtered_emails
807852
808- # we may now need to collect consent from the user, in which case, redirect
809- # to the consent-extraction-unit
810- if self ._consent_at_registration :
811- redirect_url = b"/_synapse/client/new_user_consent"
812-
813- # otherwise, redirect to the completion page
814- else :
815- redirect_url = b"/_synapse/client/sso_register"
816-
817- respond_with_redirect (request , redirect_url )
853+ respond_with_redirect (
854+ request , self ._get_url_for_next_new_user_step (session = session )
855+ )
818856
819857 async def handle_terms_accepted (
820858 self , request : Request , session_id : str , terms_version : str
@@ -842,8 +880,9 @@ async def handle_terms_accepted(
842880
843881 session .terms_accepted_version = terms_version
844882
845- # we're done; now we can register the user
846- respond_with_redirect (request , b"/_synapse/client/sso_register" )
883+ respond_with_redirect (
884+ request , self ._get_url_for_next_new_user_step (session = session )
885+ )
847886
848887 async def register_sso_user (self , request : Request , session_id : str ) -> None :
849888 """Called once we have all the info we need to register a new user.
0 commit comments