Give the user a better error when they present bad SSO creds

If a user tries to do UI Auth via SSO, but uses the wrong account on the SSO
IdP, try to give them a better error.

Previously, the UIA would claim to be successful, but then the operation in
question would simply fail with "auth fail". Instead, serve up an error page
which explains the failure.
This commit is contained in:
Richard van der Hoff 2021-01-12 18:19:42 +00:00
parent 233c8b9fce
commit 5310808d3b
5 changed files with 65 additions and 5 deletions

1
changelog.d/9091.feature Normal file
View File

@ -0,0 +1 @@
During user-interactive authentication via single-sign-on, give a better error if the user uses the wrong account on the SSO IdP.

View File

@ -1969,6 +1969,14 @@ sso:
# #
# This template has no additional variables. # This template has no additional variables.
# #
# * HTML page shown after a user-interactive authentication session which
# does not map correctly onto the expected user: 'sso_auth_bad_user.html'.
#
# When rendering, this template is given the following variables:
# * server_name: the homeserver's name.
# * user_id_to_verify: the MXID of the user that we are trying to
# validate.
#
# * HTML page shown during single sign-on if a deactivated user (according to Synapse's database) # * HTML page shown during single sign-on if a deactivated user (according to Synapse's database)
# attempts to login: 'sso_account_deactivated.html'. # attempts to login: 'sso_account_deactivated.html'.
# #

View File

@ -37,6 +37,7 @@ class SSOConfig(Config):
self.sso_error_template, self.sso_error_template,
sso_account_deactivated_template, sso_account_deactivated_template,
sso_auth_success_template, sso_auth_success_template,
self.sso_auth_bad_user_template,
) = self.read_templates( ) = self.read_templates(
[ [
"sso_login_idp_picker.html", "sso_login_idp_picker.html",
@ -45,6 +46,7 @@ class SSOConfig(Config):
"sso_error.html", "sso_error.html",
"sso_account_deactivated.html", "sso_account_deactivated.html",
"sso_auth_success.html", "sso_auth_success.html",
"sso_auth_bad_user.html",
], ],
template_dir, template_dir,
) )
@ -160,6 +162,14 @@ class SSOConfig(Config):
# #
# This template has no additional variables. # This template has no additional variables.
# #
# * HTML page shown after a user-interactive authentication session which
# does not map correctly onto the expected user: 'sso_auth_bad_user.html'.
#
# When rendering, this template is given the following variables:
# * server_name: the homeserver's name.
# * user_id_to_verify: the MXID of the user that we are trying to
# validate.
#
# * HTML page shown during single sign-on if a deactivated user (according to Synapse's database) # * HTML page shown during single sign-on if a deactivated user (according to Synapse's database)
# attempts to login: 'sso_account_deactivated.html'. # attempts to login: 'sso_account_deactivated.html'.
# #

View File

@ -23,6 +23,7 @@ from typing_extensions import NoReturn, Protocol
from twisted.web.http import Request from twisted.web.http import Request
from synapse.api.errors import Codes, RedirectException, SynapseError from synapse.api.errors import Codes, RedirectException, SynapseError
from synapse.handlers.ui_auth import UIAuthSessionDataConstants
from synapse.http import get_request_user_agent from synapse.http import get_request_user_agent
from synapse.http.server import respond_with_html from synapse.http.server import respond_with_html
from synapse.http.site import SynapseRequest from synapse.http.site import SynapseRequest
@ -147,6 +148,7 @@ class SsoHandler:
self._server_name = hs.hostname self._server_name = hs.hostname
self._registration_handler = hs.get_registration_handler() self._registration_handler = hs.get_registration_handler()
self._error_template = hs.config.sso_error_template self._error_template = hs.config.sso_error_template
self._bad_user_template = hs.config.sso_auth_bad_user_template
self._auth_handler = hs.get_auth_handler() self._auth_handler = hs.get_auth_handler()
# a lock on the mappings # a lock on the mappings
@ -577,19 +579,40 @@ class SsoHandler:
auth_provider_id, remote_user_id, auth_provider_id, remote_user_id,
) )
user_id_to_verify = await self._auth_handler.get_session_data(
ui_auth_session_id, UIAuthSessionDataConstants.REQUEST_USER_ID
) # type: str
if not user_id: if not user_id:
logger.warning( logger.warning(
"Remote user %s/%s has not previously logged in here: UIA will fail", "Remote user %s/%s has not previously logged in here: UIA will fail",
auth_provider_id, auth_provider_id,
remote_user_id, remote_user_id,
) )
# Let the UIA flow handle this the same as if they presented creds for a elif user_id != user_id_to_verify:
# different user. logger.warning(
user_id = "" "Remote user %s/%s mapped onto incorrect user %s: UIA will fail",
auth_provider_id,
remote_user_id,
user_id,
)
else:
# success!
await self._auth_handler.complete_sso_ui_auth( await self._auth_handler.complete_sso_ui_auth(
user_id, ui_auth_session_id, request user_id, ui_auth_session_id, request
) )
return
# the user_id didn't match: mark the stage of the authentication as unsuccessful
await self._store.mark_ui_auth_stage_complete(
ui_auth_session_id, LoginType.SSO, ""
)
# render an error page.
html = self._bad_user_template.render(
server_name=self._server_name, user_id_to_verify=user_id_to_verify,
)
respond_with_html(request, 200, html)
async def check_username_availability( async def check_username_availability(
self, localpart: str, session_id: str, self, localpart: str, session_id: str,

View File

@ -0,0 +1,18 @@
<html>
<head>
<title>Authentication Failed</title>
</head>
<body>
<div>
<p>
We were unable to validate your <tt>{{server_name | e}}</tt> account via
single-sign-on (SSO), because the SSO Identity Provider returned
different details than when you logged in.
</p>
<p>
Try the operation again, and ensure that you use the same details on
the Identity Provider as when you log into your account.
</p>
</div>
</body>
</html>