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

Commit 66f2444

Browse files
authored
Improve performance of the register endpoint (#8009)
1 parent 079bc3c commit 66f2444

File tree

6 files changed

+146
-74
lines changed

6 files changed

+146
-74
lines changed

changelog.d/8009.misc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Improve the performance of the register endpoint.

synapse/api/errors.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,14 +238,16 @@ class InteractiveAuthIncompleteError(Exception):
238238
(This indicates we should return a 401 with 'result' as the body)
239239
240240
Attributes:
241+
session_id: The ID of the ongoing interactive auth session.
241242
result: the server response to the request, which should be
242243
passed back to the client
243244
"""
244245

245-
def __init__(self, result: "JsonDict"):
246+
def __init__(self, session_id: str, result: "JsonDict"):
246247
super(InteractiveAuthIncompleteError, self).__init__(
247248
"Interactive auth not yet complete"
248249
)
250+
self.session_id = session_id
249251
self.result = result
250252

251253

synapse/handlers/auth.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ async def validate_user_via_ui_auth(
162162
request_body: Dict[str, Any],
163163
clientip: str,
164164
description: str,
165-
) -> dict:
165+
) -> Tuple[dict, str]:
166166
"""
167167
Checks that the user is who they claim to be, via a UI auth.
168168
@@ -183,9 +183,14 @@ async def validate_user_via_ui_auth(
183183
describes the operation happening on their account.
184184
185185
Returns:
186-
The parameters for this request (which may
186+
A tuple of (params, session_id).
187+
188+
'params' contains the parameters for this request (which may
187189
have been given only in a previous call).
188190
191+
'session_id' is the ID of this session, either passed in by the
192+
client or assigned by this call
193+
189194
Raises:
190195
InteractiveAuthIncompleteError if the client has not yet completed
191196
any of the permitted login flows
@@ -207,7 +212,7 @@ async def validate_user_via_ui_auth(
207212
flows = [[login_type] for login_type in self._supported_ui_auth_types]
208213

209214
try:
210-
result, params, _ = await self.check_auth(
215+
result, params, session_id = await self.check_ui_auth(
211216
flows, request, request_body, clientip, description
212217
)
213218
except LoginError:
@@ -230,7 +235,7 @@ async def validate_user_via_ui_auth(
230235
if user_id != requester.user.to_string():
231236
raise AuthError(403, "Invalid auth")
232237

233-
return params
238+
return params, session_id
234239

235240
def get_enabled_auth_types(self):
236241
"""Return the enabled user-interactive authentication types
@@ -240,7 +245,7 @@ def get_enabled_auth_types(self):
240245
"""
241246
return self.checkers.keys()
242247

243-
async def check_auth(
248+
async def check_ui_auth(
244249
self,
245250
flows: List[List[str]],
246251
request: SynapseRequest,
@@ -363,7 +368,7 @@ async def check_auth(
363368

364369
if not authdict:
365370
raise InteractiveAuthIncompleteError(
366-
self._auth_dict_for_flows(flows, session.session_id)
371+
session.session_id, self._auth_dict_for_flows(flows, session.session_id)
367372
)
368373

369374
# check auth type currently being presented
@@ -410,7 +415,7 @@ async def check_auth(
410415
ret = self._auth_dict_for_flows(flows, session.session_id)
411416
ret["completed"] = list(creds)
412417
ret.update(errordict)
413-
raise InteractiveAuthIncompleteError(ret)
418+
raise InteractiveAuthIncompleteError(session.session_id, ret)
414419

415420
async def add_oob_auth(
416421
self, stagetype: str, authdict: Dict[str, Any], clientip: str

synapse/rest/client/v2_alpha/account.py

Lines changed: 60 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,12 @@
1818
from http import HTTPStatus
1919

2020
from synapse.api.constants import LoginType
21-
from synapse.api.errors import Codes, SynapseError, ThreepidValidationError
21+
from synapse.api.errors import (
22+
Codes,
23+
InteractiveAuthIncompleteError,
24+
SynapseError,
25+
ThreepidValidationError,
26+
)
2227
from synapse.config.emailconfig import ThreepidBehaviour
2328
from synapse.http.server import finish_request, respond_with_html
2429
from synapse.http.servlet import (
@@ -239,18 +244,12 @@ async def on_POST(self, request):
239244

240245
# we do basic sanity checks here because the auth layer will store these
241246
# in sessions. Pull out the new password provided to us.
242-
if "new_password" in body:
243-
new_password = body.pop("new_password")
247+
new_password = body.pop("new_password", None)
248+
if new_password is not None:
244249
if not isinstance(new_password, str) or len(new_password) > 512:
245250
raise SynapseError(400, "Invalid password")
246251
self.password_policy_handler.validate_password(new_password)
247252

248-
# If the password is valid, hash it and store it back on the body.
249-
# This ensures that only the hashed password is handled everywhere.
250-
if "new_password_hash" in body:
251-
raise SynapseError(400, "Unexpected property: new_password_hash")
252-
body["new_password_hash"] = await self.auth_handler.hash(new_password)
253-
254253
# there are two possibilities here. Either the user does not have an
255254
# access token, and needs to do a password reset; or they have one and
256255
# need to validate their identity.
@@ -263,23 +262,49 @@ async def on_POST(self, request):
263262

264263
if self.auth.has_access_token(request):
265264
requester = await self.auth.get_user_by_req(request)
266-
params = await self.auth_handler.validate_user_via_ui_auth(
267-
requester,
268-
request,
269-
body,
270-
self.hs.get_ip_from_request(request),
271-
"modify your account password",
272-
)
265+
try:
266+
params, session_id = await self.auth_handler.validate_user_via_ui_auth(
267+
requester,
268+
request,
269+
body,
270+
self.hs.get_ip_from_request(request),
271+
"modify your account password",
272+
)
273+
except InteractiveAuthIncompleteError as e:
274+
# The user needs to provide more steps to complete auth, but
275+
# they're not required to provide the password again.
276+
#
277+
# If a password is available now, hash the provided password and
278+
# store it for later.
279+
if new_password:
280+
password_hash = await self.auth_handler.hash(new_password)
281+
await self.auth_handler.set_session_data(
282+
e.session_id, "password_hash", password_hash
283+
)
284+
raise
273285
user_id = requester.user.to_string()
274286
else:
275287
requester = None
276-
result, params, _ = await self.auth_handler.check_auth(
277-
[[LoginType.EMAIL_IDENTITY]],
278-
request,
279-
body,
280-
self.hs.get_ip_from_request(request),
281-
"modify your account password",
282-
)
288+
try:
289+
result, params, session_id = await self.auth_handler.check_ui_auth(
290+
[[LoginType.EMAIL_IDENTITY]],
291+
request,
292+
body,
293+
self.hs.get_ip_from_request(request),
294+
"modify your account password",
295+
)
296+
except InteractiveAuthIncompleteError as e:
297+
# The user needs to provide more steps to complete auth, but
298+
# they're not required to provide the password again.
299+
#
300+
# If a password is available now, hash the provided password and
301+
# store it for later.
302+
if new_password:
303+
password_hash = await self.auth_handler.hash(new_password)
304+
await self.auth_handler.set_session_data(
305+
e.session_id, "password_hash", password_hash
306+
)
307+
raise
283308

284309
if LoginType.EMAIL_IDENTITY in result:
285310
threepid = result[LoginType.EMAIL_IDENTITY]
@@ -304,12 +329,21 @@ async def on_POST(self, request):
304329
logger.error("Auth succeeded but no known type! %r", result.keys())
305330
raise SynapseError(500, "", Codes.UNKNOWN)
306331

307-
assert_params_in_dict(params, ["new_password_hash"])
308-
new_password_hash = params["new_password_hash"]
332+
# If we have a password in this request, prefer it. Otherwise, there
333+
# must be a password hash from an earlier request.
334+
if new_password:
335+
password_hash = await self.auth_handler.hash(new_password)
336+
else:
337+
password_hash = await self.auth_handler.get_session_data(
338+
session_id, "password_hash", None
339+
)
340+
if not password_hash:
341+
raise SynapseError(400, "Missing params: password", Codes.MISSING_PARAM)
342+
309343
logout_devices = params.get("logout_devices", True)
310344

311345
await self._set_password_handler.set_password(
312-
user_id, new_password_hash, logout_devices, requester
346+
user_id, password_hash, logout_devices, requester
313347
)
314348

315349
return 200, {}

0 commit comments

Comments
 (0)