Update the SSO username picker template to comply with SIWA guidelines (#12210)
Fixes https://github.com/matrix-org/synapse/issues/12205
This commit is contained in:
parent
32c828d0f7
commit
003cc6910a
|
@ -0,0 +1 @@
|
||||||
|
Update the SSO username picker template to comply with SIWA guidelines.
|
|
@ -1947,8 +1947,13 @@ saml2_config:
|
||||||
#
|
#
|
||||||
# localpart_template: Jinja2 template for the localpart of the MXID.
|
# localpart_template: Jinja2 template for the localpart of the MXID.
|
||||||
# If this is not set, the user will be prompted to choose their
|
# If this is not set, the user will be prompted to choose their
|
||||||
# own username (see 'sso_auth_account_details.html' in the 'sso'
|
# own username (see the documentation for the
|
||||||
# section of this file).
|
# 'sso_auth_account_details.html' template).
|
||||||
|
#
|
||||||
|
# confirm_localpart: Whether to prompt the user to validate (or
|
||||||
|
# change) the generated localpart (see the documentation for the
|
||||||
|
# 'sso_auth_account_details.html' template), instead of
|
||||||
|
# registering the account right away.
|
||||||
#
|
#
|
||||||
# display_name_template: Jinja2 template for the display name to set
|
# display_name_template: Jinja2 template for the display name to set
|
||||||
# on first login. If unset, no displayname will be set.
|
# on first login. If unset, no displayname will be set.
|
||||||
|
|
|
@ -176,8 +176,11 @@ Below are the templates Synapse will look for when generating pages related to S
|
||||||
for the brand of the IdP
|
for the brand of the IdP
|
||||||
* `user_attributes`: an object containing details about the user that
|
* `user_attributes`: an object containing details about the user that
|
||||||
we received from the IdP. May have the following attributes:
|
we received from the IdP. May have the following attributes:
|
||||||
* display_name: the user's display_name
|
* `display_name`: the user's display name
|
||||||
* emails: a list of email addresses
|
* `emails`: a list of email addresses
|
||||||
|
* `localpart`: the local part of the Matrix user ID to register,
|
||||||
|
if `localpart_template` is set in the mapping provider configuration (empty
|
||||||
|
string if not)
|
||||||
The template should render a form which submits the following fields:
|
The template should render a form which submits the following fields:
|
||||||
* `username`: the localpart of the user's chosen user id
|
* `username`: the localpart of the user's chosen user id
|
||||||
* `sso_new_user_consent.html`: HTML page allowing the user to consent to the
|
* `sso_new_user_consent.html`: HTML page allowing the user to consent to the
|
||||||
|
|
|
@ -182,8 +182,13 @@ class OIDCConfig(Config):
|
||||||
#
|
#
|
||||||
# localpart_template: Jinja2 template for the localpart of the MXID.
|
# localpart_template: Jinja2 template for the localpart of the MXID.
|
||||||
# If this is not set, the user will be prompted to choose their
|
# If this is not set, the user will be prompted to choose their
|
||||||
# own username (see 'sso_auth_account_details.html' in the 'sso'
|
# own username (see the documentation for the
|
||||||
# section of this file).
|
# 'sso_auth_account_details.html' template).
|
||||||
|
#
|
||||||
|
# confirm_localpart: Whether to prompt the user to validate (or
|
||||||
|
# change) the generated localpart (see the documentation for the
|
||||||
|
# 'sso_auth_account_details.html' template), instead of
|
||||||
|
# registering the account right away.
|
||||||
#
|
#
|
||||||
# display_name_template: Jinja2 template for the display name to set
|
# display_name_template: Jinja2 template for the display name to set
|
||||||
# on first login. If unset, no displayname will be set.
|
# on first login. If unset, no displayname will be set.
|
||||||
|
|
|
@ -1228,6 +1228,7 @@ class OidcSessionData:
|
||||||
|
|
||||||
class UserAttributeDict(TypedDict):
|
class UserAttributeDict(TypedDict):
|
||||||
localpart: Optional[str]
|
localpart: Optional[str]
|
||||||
|
confirm_localpart: bool
|
||||||
display_name: Optional[str]
|
display_name: Optional[str]
|
||||||
emails: List[str]
|
emails: List[str]
|
||||||
|
|
||||||
|
@ -1316,6 +1317,7 @@ class JinjaOidcMappingConfig:
|
||||||
display_name_template: Optional[Template]
|
display_name_template: Optional[Template]
|
||||||
email_template: Optional[Template]
|
email_template: Optional[Template]
|
||||||
extra_attributes: Dict[str, Template]
|
extra_attributes: Dict[str, Template]
|
||||||
|
confirm_localpart: bool = False
|
||||||
|
|
||||||
|
|
||||||
class JinjaOidcMappingProvider(OidcMappingProvider[JinjaOidcMappingConfig]):
|
class JinjaOidcMappingProvider(OidcMappingProvider[JinjaOidcMappingConfig]):
|
||||||
|
@ -1357,12 +1359,17 @@ class JinjaOidcMappingProvider(OidcMappingProvider[JinjaOidcMappingConfig]):
|
||||||
"invalid jinja template", path=["extra_attributes", key]
|
"invalid jinja template", path=["extra_attributes", key]
|
||||||
) from e
|
) from e
|
||||||
|
|
||||||
|
confirm_localpart = config.get("confirm_localpart") or False
|
||||||
|
if not isinstance(confirm_localpart, bool):
|
||||||
|
raise ConfigError("must be a bool", path=["confirm_localpart"])
|
||||||
|
|
||||||
return JinjaOidcMappingConfig(
|
return JinjaOidcMappingConfig(
|
||||||
subject_claim=subject_claim,
|
subject_claim=subject_claim,
|
||||||
localpart_template=localpart_template,
|
localpart_template=localpart_template,
|
||||||
display_name_template=display_name_template,
|
display_name_template=display_name_template,
|
||||||
email_template=email_template,
|
email_template=email_template,
|
||||||
extra_attributes=extra_attributes,
|
extra_attributes=extra_attributes,
|
||||||
|
confirm_localpart=confirm_localpart,
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_remote_user_id(self, userinfo: UserInfo) -> str:
|
def get_remote_user_id(self, userinfo: UserInfo) -> str:
|
||||||
|
@ -1398,7 +1405,10 @@ class JinjaOidcMappingProvider(OidcMappingProvider[JinjaOidcMappingConfig]):
|
||||||
emails.append(email)
|
emails.append(email)
|
||||||
|
|
||||||
return UserAttributeDict(
|
return UserAttributeDict(
|
||||||
localpart=localpart, display_name=display_name, emails=emails
|
localpart=localpart,
|
||||||
|
display_name=display_name,
|
||||||
|
emails=emails,
|
||||||
|
confirm_localpart=self._config.confirm_localpart,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def get_extra_attributes(self, userinfo: UserInfo, token: Token) -> JsonDict:
|
async def get_extra_attributes(self, userinfo: UserInfo, token: Token) -> JsonDict:
|
||||||
|
|
|
@ -132,6 +132,7 @@ class UserAttributes:
|
||||||
# if `None`, the mapper has not picked a userid, and the user should be prompted to
|
# if `None`, the mapper has not picked a userid, and the user should be prompted to
|
||||||
# enter one.
|
# enter one.
|
||||||
localpart: Optional[str]
|
localpart: Optional[str]
|
||||||
|
confirm_localpart: bool = False
|
||||||
display_name: Optional[str] = None
|
display_name: Optional[str] = None
|
||||||
emails: Collection[str] = attr.Factory(list)
|
emails: Collection[str] = attr.Factory(list)
|
||||||
|
|
||||||
|
@ -561,9 +562,10 @@ class SsoHandler:
|
||||||
# Must provide either attributes or session, not both
|
# Must provide either attributes or session, not both
|
||||||
assert (attributes is not None) != (session is not None)
|
assert (attributes is not None) != (session is not None)
|
||||||
|
|
||||||
if (attributes and attributes.localpart is None) or (
|
if (
|
||||||
session and session.chosen_localpart is None
|
attributes
|
||||||
):
|
and (attributes.localpart is None or attributes.confirm_localpart is True)
|
||||||
|
) or (session and session.chosen_localpart is None):
|
||||||
return b"/_synapse/client/pick_username/account_details"
|
return b"/_synapse/client/pick_username/account_details"
|
||||||
elif self._consent_at_registration and not (
|
elif self._consent_at_registration and not (
|
||||||
session and session.terms_accepted_version
|
session and session.terms_accepted_version
|
||||||
|
|
|
@ -130,15 +130,15 @@
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
<h1>Your account is nearly ready</h1>
|
<h1>Choose your user name</h1>
|
||||||
<p>Check your details before creating an account on {{ server_name }}</p>
|
<p>This is required to create your account on {{ server_name }}, and you can't change this later.</p>
|
||||||
</header>
|
</header>
|
||||||
<main>
|
<main>
|
||||||
<form method="post" class="form__input" id="form">
|
<form method="post" class="form__input" id="form">
|
||||||
<div class="username_input" id="username_input">
|
<div class="username_input" id="username_input">
|
||||||
<label for="field-username">Username</label>
|
<label for="field-username">Username</label>
|
||||||
<div class="prefix">@</div>
|
<div class="prefix">@</div>
|
||||||
<input type="text" name="username" id="field-username" autofocus>
|
<input type="text" name="username" id="field-username" value="{{ user_attributes.localpart }}" autofocus>
|
||||||
<div class="postfix">:{{ server_name }}</div>
|
<div class="postfix">:{{ server_name }}</div>
|
||||||
</div>
|
</div>
|
||||||
<output for="username_input" id="field-username-output"></output>
|
<output for="username_input" id="field-username-output"></output>
|
||||||
|
|
|
@ -92,12 +92,20 @@ class AccountDetailsResource(DirectServeHtmlResource):
|
||||||
self._sso_handler.render_error(request, "bad_session", e.msg, code=e.code)
|
self._sso_handler.render_error(request, "bad_session", e.msg, code=e.code)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# The configuration might mandate going through this step to validate an
|
||||||
|
# automatically generated localpart, so session.chosen_localpart might already
|
||||||
|
# be set.
|
||||||
|
localpart = ""
|
||||||
|
if session.chosen_localpart is not None:
|
||||||
|
localpart = session.chosen_localpart
|
||||||
|
|
||||||
idp_id = session.auth_provider_id
|
idp_id = session.auth_provider_id
|
||||||
template_params = {
|
template_params = {
|
||||||
"idp": self._sso_handler.get_identity_providers()[idp_id],
|
"idp": self._sso_handler.get_identity_providers()[idp_id],
|
||||||
"user_attributes": {
|
"user_attributes": {
|
||||||
"display_name": session.display_name,
|
"display_name": session.display_name,
|
||||||
"emails": session.emails,
|
"emails": session.emails,
|
||||||
|
"localpart": localpart,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue