Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit 0c0da36

Browse files
authored
Ask consent on SSO registration with default mxid (#10733)
Fixes #10732: consent flow skipped during SSO user registration if username is left at default Signed-off-by: Andrew Ferrazzutti [email protected]
1 parent 7f0565e commit 0c0da36

File tree

3 files changed

+63
-23
lines changed

3 files changed

+63
-23
lines changed

changelog.d/10733.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Allow user registration via SSO to require consent tracking for SSO mapping providers that don't prompt for Matrix ID selection. Contributed by @AndrewFerr.

synapse/handlers/sso.py

Lines changed: 60 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -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.

synapse/rest/synapse/client/new_user_consent.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ async def _async_render_GET(self, request: Request) -> None:
6363
self._sso_handler.render_error(request, "bad_session", e.msg, code=e.code)
6464
return
6565

66-
# It should be impossible to get here without having first been through
67-
# the pick-a-username step, which ensures chosen_localpart gets set.
66+
# It should be impossible to get here without either the user or the mapping provider
67+
# having chosen a username, which ensures chosen_localpart gets set.
6868
if not session.chosen_localpart:
6969
logger.warning("Session has no user name selected")
7070
self._sso_handler.render_error(

0 commit comments

Comments
 (0)