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

Show a confirmation page during user password reset #8004

Merged
merged 36 commits into from
Sep 10, 2020
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
9003eb4
Add confirmation page template
anoadragon453 Aug 21, 2020
5f7a834
Load the new template
anoadragon453 Jul 30, 2020
d9a19fc
Create new password reset confirmation endpoint
anoadragon453 Aug 21, 2020
6140b32
Add confirmation as a step to the password reset unit tests
anoadragon453 Aug 21, 2020
622946e
Remind people about the new template in the upgrade notes
anoadragon453 Aug 21, 2020
3568e18
Add changelog
anoadragon453 Jul 31, 2020
8cc8ee4
Remove unused fields
anoadragon453 Aug 21, 2020
b41b051
typing
anoadragon453 Aug 21, 2020
0a2b11f
Ensure we don't fail a request due to the config
anoadragon453 Aug 21, 2020
1d79f7b
Remove another unused class var
anoadragon453 Aug 21, 2020
3f4e350
Merge branch 'develop' of github.com:matrix-org/synapse into anoa/pas…
anoadragon453 Aug 26, 2020
d6addba
Update synapse/rest/client/v2_alpha/account.py
anoadragon453 Aug 28, 2020
990178f
Pull things from, instead of copying the entirety of, the config
anoadragon453 Aug 28, 2020
1b4458e
Return 400 when accessing submit_token/_confirm with REMOTE behaviour
anoadragon453 Aug 28, 2020
0608fe1
Convert confirmation from request args to HTML form data
anoadragon453 Aug 28, 2020
d9f5b4c
Combine the submit_token and submit_token_confirm endpoints
anoadragon453 Aug 28, 2020
f25209c
Switch from an unstable /_matrix/client prefix to /_synapse/client
anoadragon453 Aug 28, 2020
5060a8e
Update UPGRADE.rst
anoadragon453 Sep 1, 2020
8711981
Merge branch 'develop' of github.com:matrix-org/synapse into anoa/pas…
anoadragon453 Sep 2, 2020
1c1dc03
Merge branch 'anoa/password_reset_confirmation' of github.com:matrix-…
anoadragon453 Sep 2, 2020
9d90642
Add new resource to password reset unit tests
anoadragon453 Sep 2, 2020
7073605
Move PasswordResetSubmitTokenServlet to new synapse/client resource
anoadragon453 Sep 2, 2020
8efa7ac
Move synapse_client_patterns to synapse/client resource folder
anoadragon453 Sep 2, 2020
ad231e3
Add new PasswordResetRestResource JsonResource
anoadragon453 Sep 2, 2020
c1b9ad2
Add PasswordResetRestResource to HomeServer
anoadragon453 Sep 2, 2020
cb7c903
Convert synapse.rest.synapse to a python package
anoadragon453 Sep 3, 2020
76e9000
Rename synapse module to internal due to conflicts
anoadragon453 Sep 3, 2020
717961d
Remove action call from HTML template, remove medium placeholder
anoadragon453 Sep 3, 2020
775e310
Update docstring
anoadragon453 Sep 3, 2020
5ed7cf7
Switch from serlvets to base resources
anoadragon453 Sep 3, 2020
e2f0574
Merge branch 'develop' of github.com:matrix-org/synapse into anoa/pas…
anoadragon453 Sep 9, 2020
c3f6ff5
Inline password reset confirmation template filename
anoadragon453 Sep 9, 2020
2b6bde7
Typing and returning things directly
anoadragon453 Sep 9, 2020
0ad0753
Remove checks for threepid behaviour and add an assert
anoadragon453 Sep 9, 2020
c460e3b
v1.20.0 -> v1.21.0
anoadragon453 Sep 9, 2020
c5af421
Rename 'internal' dir to 'synapse'
anoadragon453 Sep 9, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions UPGRADE.rst
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,30 @@ for example:
wget https://packages.matrix.org/debian/pool/main/m/matrix-synapse-py3/matrix-synapse-py3_1.3.0+stretch1_amd64.deb
dpkg -i matrix-synapse-py3_1.3.0+stretch1_amd64.deb

Upgrading to v1.20.0
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
====================

New HTML templates
------------------

A new HTML template,
`password_reset_confirmation.html <https://github.com/matrix-org/synapse/blob/develop/synapse/res/templates/password_reset_confirmation.html>`_,
has been added to the ``synapse/res/templates`` directory. If you are using a
custom template directory, you may want to copy the template over and modify it.

Note that as of v1.20.0, templates do not need to be included in custom template
directories for Synapse to start. The default templates will be used if a custom
template cannot be found.

This page will appear to the user after clicking a password reset link that has
been emailed to them.

To complete password reset, the page must include a way to make a `POST`
request to
``/_matrix/client/unstable/password_reset/{medium}/submit_token_confirm``
with the query parameters from the original link. See the file itself for more
details.

Upgrading to v1.18.0
====================

Expand Down
1 change: 1 addition & 0 deletions changelog.d/8004.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Require the user to confirm that their password should be reset after clicking the email confirmation link.
10 changes: 7 additions & 3 deletions docs/sample_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2015,9 +2015,13 @@ email:
# * The contents of password reset emails sent by the homeserver:
# 'password_reset.html' and 'password_reset.txt'
#
# * HTML pages for success and failure that a user will see when they follow
# the link in the password reset email: 'password_reset_success.html' and
# 'password_reset_failure.html'
# * An HTML page that a user will see when they follow the link in the password
# reset email. The user will be asked to confirm the action before their
# password is reset: 'password_reset_confirmation.html'
#
# * HTML pages for success and failure that a user will see when they confirm
# the password reset flow using the page above: 'password_reset_success.html'
# and 'password_reset_failure.html'
#
# * The contents of address verification emails sent during registration:
# 'registration.html' and 'registration.txt'
Expand Down
15 changes: 12 additions & 3 deletions synapse/config/emailconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,9 @@ def read_config(self, config, **kwargs):
"add_threepid_template_text", "add_threepid.txt"
)

password_reset_template_confirmation_html = (
"password_reset_confirmation.html"
)
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
password_reset_template_failure_html = email_config.get(
"password_reset_template_failure_html", "password_reset_failure.html"
)
Expand Down Expand Up @@ -228,6 +231,7 @@ def read_config(self, config, **kwargs):
self.email_registration_template_text,
self.email_add_threepid_template_html,
self.email_add_threepid_template_text,
self.email_password_reset_template_confirmation_html,
self.email_password_reset_template_failure_html,
self.email_registration_template_failure_html,
self.email_add_threepid_template_failure_html,
Expand All @@ -242,6 +246,7 @@ def read_config(self, config, **kwargs):
registration_template_text,
add_threepid_template_html,
add_threepid_template_text,
password_reset_template_confirmation_html,
password_reset_template_failure_html,
registration_template_failure_html,
add_threepid_template_failure_html,
Expand Down Expand Up @@ -404,9 +409,13 @@ def generate_config_section(self, config_dir_path, server_name, **kwargs):
# * The contents of password reset emails sent by the homeserver:
# 'password_reset.html' and 'password_reset.txt'
#
# * HTML pages for success and failure that a user will see when they follow
# the link in the password reset email: 'password_reset_success.html' and
# 'password_reset_failure.html'
# * An HTML page that a user will see when they follow the link in the password
# reset email. The user will be asked to confirm the action before their
# password is reset: 'password_reset_confirmation.html'
#
# * HTML pages for success and failure that a user will see when they confirm
# the password reset flow using the page above: 'password_reset_success.html'
# and 'password_reset_failure.html'
#
# * The contents of address verification emails sent during registration:
# 'registration.html' and 'registration.txt'
Expand Down
16 changes: 16 additions & 0 deletions synapse/res/templates/password_reset_confirmation.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<html>
<head></head>
<body>
<!--Use a hidden form to resubmit the information necessary to reset the password-->
<form action="/_matrix/client/unstable/password_reset/{{ medium }}/submit_token_confirm" method="post">
<input type="hidden" name="sid" value="{{ sid }}">
<input type="hidden" name="token" value="{{ token }}">
<input type="hidden" name="client_secret" value="{{ client_secret }}">

<p>You have requested to <strong>reset your Matrix account password</strong>. Click the link below to confirm this action. <br /><br />
If you did not mean to do this, please close this page and your password will not be changed.</p>
<p><button type="submit">Confirm changing my password</button></p>
</form>
</body>
</html>

54 changes: 51 additions & 3 deletions synapse/rest/client/v2_alpha/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,10 @@ def __init__(self, hs):
self.config = hs.config
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
self.clock = hs.get_clock()
self.store = hs.get_datastore()

anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
if self.config.threepid_behaviour_email == ThreepidBehaviour.LOCAL:
self._failure_email_template = (
self.config.email_password_reset_template_failure_html
self._confirmation_email_template = (
self.config.email_password_reset_template_confirmation_html
)

async def on_GET(self, request, medium):
Expand All @@ -183,6 +184,52 @@ async def on_GET(self, request, medium):
client_secret = parse_string(request, "client_secret", required=True)
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
assert_valid_client_secret(client_secret)

# Show a confirmation page, just in case someone accidentally clicked this link when
# they didn't mean to
template_vars = {
"sid": sid,
"token": token,
"client_secret": client_secret,
"medium": medium,
}
respond_with_html(
request, 200, self._confirmation_email_template.render(**template_vars)
)


class PasswordResetConfirmationSubmitTokenServlet(RestServlet):
"""Handles confirmation of 3PID validation token submission.

A user will land on PasswordResetSubmitTokenServlet, confirm the password reset, then
submit the same parameters to this servlet.
"""

PATTERNS = client_patterns(
"/password_reset/email/submit_token_confirm$", releases=(), unstable=True,
)

def __init__(self, hs):
"""
Args:
hs (synapse.server.HomeServer): server
"""
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
super(PasswordResetConfirmationSubmitTokenServlet, self).__init__()
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
self.auth = hs.get_auth()
self.clock = hs.get_clock()
self.store = hs.get_datastore()
if hs.config.threepid_behaviour_email == ThreepidBehaviour.LOCAL:
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
self._failure_email_template = (
hs.config.email_password_reset_template_failure_html
)
self._email_password_reset_template_success_html = (
hs.config.email_password_reset_template_success_html_content
)

async def on_POST(self, request):
sid = parse_string(request, "sid", required=True)
token = parse_string(request, "token", required=True)
client_secret = parse_string(request, "client_secret", required=True)

# Attempt to validate a 3PID session
try:
# Mark the session as valid
Expand All @@ -203,7 +250,7 @@ async def on_GET(self, request, medium):
return None

# Otherwise show the success template
html = self.config.email_password_reset_template_success_html_content
html = self._email_password_reset_template_success_html
status_code = 200
except ThreepidValidationError as e:
status_code = e.code
Expand Down Expand Up @@ -881,6 +928,7 @@ async def on_GET(self, request):
def register_servlets(hs, http_server):
EmailPasswordRequestTokenRestServlet(hs).register(http_server)
PasswordResetSubmitTokenServlet(hs).register(http_server)
PasswordResetConfirmationSubmitTokenServlet(hs).register(http_server)
PasswordRestServlet(hs).register(http_server)
DeactivateAccountRestServlet(hs).register(http_server)
EmailThreepidRequestTokenRestServlet(hs).register(http_server)
Expand Down
13 changes: 13 additions & 0 deletions tests/rest/client/v2_alpha/test_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,10 +250,23 @@ def _validate_token(self, link):
# Remove the host
path = link.replace("https://example.com", "")

# Load the password reset confirmation page
request, channel = self.make_request("GET", path, shorthand=False)
self.render(request)
self.assertEquals(200, channel.code, channel.result)

# Replace the path with the confirmation path
path = re.sub(
"^/_matrix.*submit_token",
"/_matrix/client/unstable/password_reset/email/submit_token_confirm",
path,
)

# Confirm the password reset
request, channel = self.make_request("POST", path, shorthand=False)
self.render(request)
self.assertEquals(200, channel.code, channel.result)
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved

def _get_link_from_email(self):
assert self.email_attempts, "No emails have been sent"

Expand Down