async def try_unbind_threepid_with_id_server(self, mxid: str, threepid: dict, id_server: str) -> bool: """Removes a binding from an identity server Args: mxid: Matrix user ID of binding to be removed threepid: Dict with medium & address of binding to be removed id_server: Identity server to unbind from Raises: SynapseError: On any of the following conditions - the supplied id_server is not a valid identity server name - we failed to contact the supplied identity server Returns: True on success, otherwise False if the identity server doesn't support unbinding """ if not valid_id_server_location(id_server): raise SynapseError( 400, "id_server must be a valid hostname with optional port and path components", ) url = "https://%s/_matrix/identity/api/v1/3pid/unbind" % (id_server, ) url_bytes = b"/_matrix/identity/api/v1/3pid/unbind" content = { "mxid": mxid, "threepid": { "medium": threepid["medium"], "address": threepid["address"] }, } # we abuse the federation http client to sign the request, but we have to send it # using the normal http client since we don't want the SRV lookup and want normal # 'browser-like' HTTPS. auth_headers = self.federation_http_client.build_auth_headers( destination=None, method=b"POST", url_bytes=url_bytes, content=content, destination_is=id_server.encode("ascii"), ) headers = {b"Authorization": auth_headers} try: # Use the blacklisting http client as this call is only to identity servers # provided by a client await self.blacklisting_http_client.post_json_get_json( url, content, headers) changed = True except HttpResponseException as e: changed = False if e.code in (400, 404, 501): # The remote server probably doesn't support unbinding (yet) logger.warning("Received %d response while unbinding threepid", e.code) else: logger.error( "Failed to unbind threepid on identity server: %s", e) raise SynapseError(500, "Failed to contact identity server") except RequestTimedOutError: raise SynapseError(500, "Timed out contacting identity server") await self.store.remove_user_bound_threepid( user_id=mxid, medium=threepid["medium"], address=threepid["address"], id_server=id_server, ) return changed
async def bind_threepid( self, client_secret: str, sid: str, mxid: str, id_server: str, id_access_token: Optional[str] = None, use_v2: bool = True, ) -> JsonDict: """Bind a 3PID to an identity server Args: client_secret: A unique secret provided by the client sid: The ID of the validation session mxid: The MXID to bind the 3PID to id_server: The domain of the identity server to query id_access_token: The access token to authenticate to the identity server with, if necessary. Required if use_v2 is true use_v2: Whether to use v2 Identity Service API endpoints. Defaults to True Raises: SynapseError: On any of the following conditions - the supplied id_server is not a valid identity server name - we failed to contact the supplied identity server Returns: The response from the identity server """ logger.debug("Proxying threepid bind request for %s to %s", mxid, id_server) # If an id_access_token is not supplied, force usage of v1 if id_access_token is None: use_v2 = False if not valid_id_server_location(id_server): raise SynapseError( 400, "id_server must be a valid hostname with optional port and path components", ) # Decide which API endpoint URLs to use headers = {} bind_data = {"sid": sid, "client_secret": client_secret, "mxid": mxid} if use_v2: bind_url = "https://%s/_matrix/identity/v2/3pid/bind" % ( id_server, ) headers["Authorization"] = create_id_access_token_header( id_access_token) # type: ignore else: bind_url = "https://%s/_matrix/identity/api/v1/3pid/bind" % ( id_server, ) try: # Use the blacklisting http client as this call is only to identity servers # provided by a client data = await self.blacklisting_http_client.post_json_get_json( bind_url, bind_data, headers=headers) # Remember where we bound the threepid await self.store.add_user_bound_threepid( user_id=mxid, medium=data["medium"], address=data["address"], id_server=id_server, ) return data except HttpResponseException as e: if e.code != 404 or not use_v2: logger.error("3PID bind failed with Matrix error: %r", e) raise e.to_synapse_error() except RequestTimedOutError: raise SynapseError(500, "Timed out contacting identity server") except CodeMessageException as e: data = json_decoder.decode(e.msg) # XXX WAT? return data logger.info("Got 404 when POSTing JSON %s, falling back to v1 URL", bind_url) res = await self.bind_threepid(client_secret, sid, mxid, id_server, id_access_token, use_v2=False) return res