@@ -252,6 +252,7 @@ async def check_auth(
252252 clientdict : Dict [str , Any ],
253253 clientip : str ,
254254 description : str ,
255+ validate_clientdict : bool = True ,
255256 ) -> Tuple [dict , dict , str ]:
256257 """
257258 Takes a dictionary sent by the client in the login / registration
@@ -277,6 +278,10 @@ async def check_auth(
277278 description: A human readable string to be displayed to the user that
278279 describes the operation happening on their account.
279280
281+ validate_clientdict: Whether to validate that the operation happening
282+ on the account has not changed. If this is false,
283+ the client dict is persisted instead of validated.
284+
280285 Returns:
281286 A tuple of (creds, params, session_id).
282287
@@ -317,30 +322,51 @@ async def check_auth(
317322 except StoreError :
318323 raise SynapseError (400 , "Unknown session ID: %s" % (sid ,))
319324
325+ # If the client provides parameters, update what is persisted,
326+ # otherwise use whatever was last provided.
327+ #
328+ # This was designed to allow the client to omit the parameters
329+ # and just supply the session in subsequent calls so it split
330+ # auth between devices by just sharing the session, (eg. so you
331+ # could continue registration from your phone having clicked the
332+ # email auth link on there). It's probably too open to abuse
333+ # because it lets unauthenticated clients store arbitrary objects
334+ # on a homeserver.
335+ #
336+ # Revisit: Assuming the REST APIs do sensible validation, the data
337+ # isn't arbitrary.
338+ #
339+ # Note that the registration endpoint explicitly removes the
340+ # "initial_device_display_name" parameter if it is provided
341+ # without a "password" parameter. See the changes to
342+ # synapse.rest.client.v2_alpha.register.RegisterRestServlet.on_POST
343+ # in commit 544722bad23fc31056b9240189c3cbbbf0ffd3f9.
320344 if not clientdict :
321- # This was designed to allow the client to omit the parameters
322- # and just supply the session in subsequent calls so it split
323- # auth between devices by just sharing the session, (eg. so you
324- # could continue registration from your phone having clicked the
325- # email auth link on there). It's probably too open to abuse
326- # because it lets unauthenticated clients store arbitrary objects
327- # on a homeserver.
328- # Revisit: Assuming the REST APIs do sensible validation, the data
329- # isn't arbitrary.
330345 clientdict = session .clientdict
331346
332347 # Ensure that the queried operation does not vary between stages of
333348 # the UI authentication session. This is done by generating a stable
334- # comparator based on the URI, method, and body (minus the auth dict)
335- # and storing it during the initial query. Subsequent queries ensure
336- # that this comparator has not changed.
337- comparator = (uri , method , clientdict )
338- if (session .uri , session .method , session .clientdict ) != comparator :
349+ # comparator based on the URI, method, and client dict (minus the
350+ # auth dict) and storing it during the initial query. Subsequent
351+ # queries ensure that this comparator has not changed.
352+ if validate_clientdict :
353+ session_comparator = (session .uri , session .method , session .clientdict )
354+ comparator = (uri , method , clientdict )
355+ else :
356+ session_comparator = (session .uri , session .method ) # type: ignore
357+ comparator = (uri , method ) # type: ignore
358+
359+ if session_comparator != comparator :
339360 raise SynapseError (
340361 403 ,
341362 "Requested operation has changed during the UI authentication session." ,
342363 )
343364
365+ # For backwards compatibility the registration endpoint persists
366+ # changes to the client dict instead of validating them.
367+ if not validate_clientdict :
368+ await self .store .set_ui_auth_clientdict (sid , clientdict )
369+
344370 if not authdict :
345371 raise InteractiveAuthIncompleteError (
346372 self ._auth_dict_for_flows (flows , session .session_id )
0 commit comments