mirror of
https://github.com/element-hq/synapse.git
synced 2024-11-27 20:22:07 +03:00
Display an error page during failure of fallback UIA. (#10561)
This commit is contained in:
parent
964f29cb6f
commit
6e613a10d0
8 changed files with 67 additions and 30 deletions
1
changelog.d/10561.bugfix
Normal file
1
changelog.d/10561.bugfix
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Display an error on User-Interactive Authentication fallback pages when authentication fails. Contributed by Callum Brown.
|
|
@ -125,6 +125,14 @@ environment variable.
|
||||||
See [using a forward proxy with Synapse documentation](setup/forward_proxy.md) for
|
See [using a forward proxy with Synapse documentation](setup/forward_proxy.md) for
|
||||||
details.
|
details.
|
||||||
|
|
||||||
|
## User-interactive authentication fallback templates can now display errors
|
||||||
|
|
||||||
|
This may affect you if you make use of custom HTML templates for the
|
||||||
|
[reCAPTCHA](../synapse/res/templates/recaptcha.html) or
|
||||||
|
[terms](../synapse/res/templates/terms.html) fallback pages.
|
||||||
|
|
||||||
|
The template is now provided an `error` variable if the authentication
|
||||||
|
process failed. See the default templates linked above for an example.
|
||||||
|
|
||||||
# Upgrading to v1.39.0
|
# Upgrading to v1.39.0
|
||||||
|
|
||||||
|
|
|
@ -627,23 +627,28 @@ class AuthHandler(BaseHandler):
|
||||||
|
|
||||||
async def add_oob_auth(
|
async def add_oob_auth(
|
||||||
self, stagetype: str, authdict: Dict[str, Any], clientip: str
|
self, stagetype: str, authdict: Dict[str, Any], clientip: str
|
||||||
) -> bool:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Adds the result of out-of-band authentication into an existing auth
|
Adds the result of out-of-band authentication into an existing auth
|
||||||
session. Currently used for adding the result of fallback auth.
|
session. Currently used for adding the result of fallback auth.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
LoginError if the stagetype is unknown or the session is missing.
|
||||||
|
LoginError is raised by check_auth if authentication fails.
|
||||||
"""
|
"""
|
||||||
if stagetype not in self.checkers:
|
if stagetype not in self.checkers:
|
||||||
raise LoginError(400, "", Codes.MISSING_PARAM)
|
raise LoginError(
|
||||||
|
400, f"Unknown UIA stage type: {stagetype}", Codes.INVALID_PARAM
|
||||||
|
)
|
||||||
if "session" not in authdict:
|
if "session" not in authdict:
|
||||||
raise LoginError(400, "", Codes.MISSING_PARAM)
|
raise LoginError(400, "Missing session ID", Codes.MISSING_PARAM)
|
||||||
|
|
||||||
|
# If authentication fails a LoginError is raised. Otherwise, store
|
||||||
|
# the successful result.
|
||||||
result = await self.checkers[stagetype].check_auth(authdict, clientip)
|
result = await self.checkers[stagetype].check_auth(authdict, clientip)
|
||||||
if result:
|
|
||||||
await self.store.mark_ui_auth_stage_complete(
|
await self.store.mark_ui_auth_stage_complete(
|
||||||
authdict["session"], stagetype, result
|
authdict["session"], stagetype, result
|
||||||
)
|
)
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def get_session_id(self, clientdict: Dict[str, Any]) -> Optional[str]:
|
def get_session_id(self, clientdict: Dict[str, Any]) -> Optional[str]:
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -49,7 +49,7 @@ class UserInteractiveAuthChecker:
|
||||||
clientip: The IP address of the client.
|
clientip: The IP address of the client.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
SynapseError if authentication failed
|
LoginError if authentication failed.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The result of authentication (to pass back to the client?)
|
The result of authentication (to pass back to the client?)
|
||||||
|
@ -131,7 +131,9 @@ class RecaptchaAuthChecker(UserInteractiveAuthChecker):
|
||||||
)
|
)
|
||||||
if resp_body["success"]:
|
if resp_body["success"]:
|
||||||
return True
|
return True
|
||||||
raise LoginError(401, "", errcode=Codes.UNAUTHORIZED)
|
raise LoginError(
|
||||||
|
401, "Captcha authentication failed", errcode=Codes.UNAUTHORIZED
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class _BaseThreepidAuthChecker:
|
class _BaseThreepidAuthChecker:
|
||||||
|
@ -191,7 +193,9 @@ class _BaseThreepidAuthChecker:
|
||||||
raise AssertionError("Unrecognized threepid medium: %s" % (medium,))
|
raise AssertionError("Unrecognized threepid medium: %s" % (medium,))
|
||||||
|
|
||||||
if not threepid:
|
if not threepid:
|
||||||
raise LoginError(401, "", errcode=Codes.UNAUTHORIZED)
|
raise LoginError(
|
||||||
|
401, "Unable to get validated threepid", errcode=Codes.UNAUTHORIZED
|
||||||
|
)
|
||||||
|
|
||||||
if threepid["medium"] != medium:
|
if threepid["medium"] != medium:
|
||||||
raise LoginError(
|
raise LoginError(
|
||||||
|
|
|
@ -16,6 +16,9 @@ function captchaDone() {
|
||||||
<body>
|
<body>
|
||||||
<form id="registrationForm" method="post" action="{{ myurl }}">
|
<form id="registrationForm" method="post" action="{{ myurl }}">
|
||||||
<div>
|
<div>
|
||||||
|
{% if error is defined %}
|
||||||
|
<p class="error"><strong>Error: {{ error }}</strong></p>
|
||||||
|
{% endif %}
|
||||||
<p>
|
<p>
|
||||||
Hello! We need to prevent computer programs and other automated
|
Hello! We need to prevent computer programs and other automated
|
||||||
things from creating accounts on this server.
|
things from creating accounts on this server.
|
||||||
|
|
|
@ -8,6 +8,9 @@
|
||||||
<body>
|
<body>
|
||||||
<form id="registrationForm" method="post" action="{{ myurl }}">
|
<form id="registrationForm" method="post" action="{{ myurl }}">
|
||||||
<div>
|
<div>
|
||||||
|
{% if error is defined %}
|
||||||
|
<p class="error"><strong>Error: {{ error }}</strong></p>
|
||||||
|
{% endif %}
|
||||||
<p>
|
<p>
|
||||||
Please click the button below if you agree to the
|
Please click the button below if you agree to the
|
||||||
<a href="{{ terms_url }}">privacy policy of this homeserver.</a>
|
<a href="{{ terms_url }}">privacy policy of this homeserver.</a>
|
||||||
|
|
|
@ -16,7 +16,7 @@ import logging
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from synapse.api.constants import LoginType
|
from synapse.api.constants import LoginType
|
||||||
from synapse.api.errors import SynapseError
|
from synapse.api.errors import LoginError, SynapseError
|
||||||
from synapse.api.urls import CLIENT_API_PREFIX
|
from synapse.api.urls import CLIENT_API_PREFIX
|
||||||
from synapse.http.server import respond_with_html
|
from synapse.http.server import respond_with_html
|
||||||
from synapse.http.servlet import RestServlet, parse_string
|
from synapse.http.servlet import RestServlet, parse_string
|
||||||
|
@ -95,29 +95,32 @@ class AuthRestServlet(RestServlet):
|
||||||
|
|
||||||
authdict = {"response": response, "session": session}
|
authdict = {"response": response, "session": session}
|
||||||
|
|
||||||
success = await self.auth_handler.add_oob_auth(
|
try:
|
||||||
|
await self.auth_handler.add_oob_auth(
|
||||||
LoginType.RECAPTCHA, authdict, request.getClientIP()
|
LoginType.RECAPTCHA, authdict, request.getClientIP()
|
||||||
)
|
)
|
||||||
|
except LoginError as e:
|
||||||
if success:
|
# Authentication failed, let user try again
|
||||||
html = self.success_template.render()
|
|
||||||
else:
|
|
||||||
html = self.recaptcha_template.render(
|
html = self.recaptcha_template.render(
|
||||||
session=session,
|
session=session,
|
||||||
myurl="%s/r0/auth/%s/fallback/web"
|
myurl="%s/r0/auth/%s/fallback/web"
|
||||||
% (CLIENT_API_PREFIX, LoginType.RECAPTCHA),
|
% (CLIENT_API_PREFIX, LoginType.RECAPTCHA),
|
||||||
sitekey=self.hs.config.recaptcha_public_key,
|
sitekey=self.hs.config.recaptcha_public_key,
|
||||||
|
error=e.msg,
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
# No LoginError was raised, so authentication was successful
|
||||||
|
html = self.success_template.render()
|
||||||
|
|
||||||
elif stagetype == LoginType.TERMS:
|
elif stagetype == LoginType.TERMS:
|
||||||
authdict = {"session": session}
|
authdict = {"session": session}
|
||||||
|
|
||||||
success = await self.auth_handler.add_oob_auth(
|
try:
|
||||||
|
await self.auth_handler.add_oob_auth(
|
||||||
LoginType.TERMS, authdict, request.getClientIP()
|
LoginType.TERMS, authdict, request.getClientIP()
|
||||||
)
|
)
|
||||||
|
except LoginError as e:
|
||||||
if success:
|
# Authentication failed, let user try again
|
||||||
html = self.success_template.render()
|
|
||||||
else:
|
|
||||||
html = self.terms_template.render(
|
html = self.terms_template.render(
|
||||||
session=session,
|
session=session,
|
||||||
terms_url="%s_matrix/consent?v=%s"
|
terms_url="%s_matrix/consent?v=%s"
|
||||||
|
@ -127,10 +130,16 @@ class AuthRestServlet(RestServlet):
|
||||||
),
|
),
|
||||||
myurl="%s/r0/auth/%s/fallback/web"
|
myurl="%s/r0/auth/%s/fallback/web"
|
||||||
% (CLIENT_API_PREFIX, LoginType.TERMS),
|
% (CLIENT_API_PREFIX, LoginType.TERMS),
|
||||||
|
error=e.msg,
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
# No LoginError was raised, so authentication was successful
|
||||||
|
html = self.success_template.render()
|
||||||
|
|
||||||
elif stagetype == LoginType.SSO:
|
elif stagetype == LoginType.SSO:
|
||||||
# The SSO fallback workflow should not post here,
|
# The SSO fallback workflow should not post here,
|
||||||
raise SynapseError(404, "Fallback SSO auth does not support POST requests.")
|
raise SynapseError(404, "Fallback SSO auth does not support POST requests.")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise SynapseError(404, "Unknown auth stage type")
|
raise SynapseError(404, "Unknown auth stage type")
|
||||||
|
|
||||||
|
|
|
@ -58,3 +58,7 @@ textarea, input {
|
||||||
background-color: #f8f8f8;
|
background-color: #f8f8f8;
|
||||||
border: 1px #ccc solid;
|
border: 1px #ccc solid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue