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

Commit e7ad777

Browse files
committed
Switch to using v2 Identity Service APIs other than lookup (MSC 2140) (#5892)
2 parents f9dfe7f + a0d294c commit e7ad777

File tree

4 files changed

+133
-47
lines changed

4 files changed

+133
-47
lines changed

changelog.d/5892.misc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Compatibility with v2 Identity Service APIs other than /lookup.

contrib/cmdclient/console.py

+5
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ def do_emailrequest(self, line):
268268

269269
@defer.inlineCallbacks
270270
def _do_emailrequest(self, args):
271+
# TODO: Update to use v2 Identity Service API endpoint
271272
url = (
272273
self._identityServerUrl()
273274
+ "/_matrix/identity/api/v1/validate/email/requestToken"
@@ -302,6 +303,7 @@ def do_emailvalidate(self, line):
302303

303304
@defer.inlineCallbacks
304305
def _do_emailvalidate(self, args):
306+
# TODO: Update to use v2 Identity Service API endpoint
305307
url = (
306308
self._identityServerUrl()
307309
+ "/_matrix/identity/api/v1/validate/email/submitToken"
@@ -330,6 +332,7 @@ def do_3pidbind(self, line):
330332

331333
@defer.inlineCallbacks
332334
def _do_3pidbind(self, args):
335+
# TODO: Update to use v2 Identity Service API endpoint
333336
url = self._identityServerUrl() + "/_matrix/identity/api/v1/3pid/bind"
334337

335338
json_res = yield self.http_client.do_request(
@@ -398,6 +401,7 @@ def do_invite(self, line):
398401
@defer.inlineCallbacks
399402
def _do_invite(self, roomid, userstring):
400403
if not userstring.startswith("@") and self._is_on("complete_usernames"):
404+
# TODO: Update to use v2 Identity Service API endpoint
401405
url = self._identityServerUrl() + "/_matrix/identity/api/v1/lookup"
402406

403407
json_res = yield self.http_client.do_request(
@@ -407,6 +411,7 @@ def _do_invite(self, roomid, userstring):
407411
mxid = None
408412

409413
if "mxid" in json_res and "signatures" in json_res:
414+
# TODO: Update to use v2 Identity Service API endpoint
410415
url = (
411416
self._identityServerUrl()
412417
+ "/_matrix/identity/api/v1/pubkey/ed25519"

synapse/handlers/identity.py

+120-42
Original file line numberDiff line numberDiff line change
@@ -68,21 +68,76 @@ def _should_trust_id_server(self, id_server):
6868
return False
6969
return True
7070

71+
def _extract_items_from_creds_dict(self, creds):
72+
"""
73+
Retrieve entries from a "credentials" dictionary
74+
75+
Args:
76+
creds (dict[str, str]): Dictionary of credentials that contain the following keys:
77+
* client_secret|clientSecret: A unique secret str provided by the client
78+
* id_server|idServer: the domain of the identity server to query
79+
* id_access_token: The access token to authenticate to the identity
80+
server with.
81+
82+
Returns:
83+
tuple(str, str, str|None): A tuple containing the client_secret, the id_server,
84+
and the id_access_token value if available.
85+
"""
86+
client_secret = creds.get("client_secret") or creds.get("clientSecret")
87+
if not client_secret:
88+
raise SynapseError(
89+
400, "No client_secret in creds", errcode=Codes.MISSING_PARAM
90+
)
91+
92+
id_server = creds.get("id_server") or creds.get("idServer")
93+
if not id_server:
94+
raise SynapseError(
95+
400, "No id_server in creds", errcode=Codes.MISSING_PARAM
96+
)
97+
98+
id_access_token = creds.get("id_access_token")
99+
return client_secret, id_server, id_access_token
100+
71101
@defer.inlineCallbacks
72-
def threepid_from_creds(self, creds):
73-
if "id_server" in creds:
74-
id_server = creds["id_server"]
75-
elif "idServer" in creds:
76-
id_server = creds["idServer"]
77-
else:
78-
raise SynapseError(400, "No id_server in creds")
102+
def threepid_from_creds(self, creds, use_v2=True):
103+
"""
104+
Retrieve and validate a threepid identitier from a "credentials" dictionary
105+
106+
Args:
107+
creds (dict[str, str]): Dictionary of credentials that contain the following keys:
108+
* client_secret|clientSecret: A unique secret str provided by the client
109+
* id_server|idServer: the domain of the identity server to query
110+
* id_access_token: The access token to authenticate to the identity
111+
server with. Required if use_v2 is true
112+
use_v2 (bool): Whether to use v2 Identity Service API endpoints
113+
114+
Returns:
115+
Deferred[dict[str,str|int]|None]: A dictionary consisting of response params to
116+
the /getValidated3pid endpoint of the Identity Service API, or None if the
117+
threepid was not found
118+
"""
119+
client_secret, id_server, id_access_token = self._extract_items_from_creds_dict(
120+
creds
121+
)
122+
123+
# If an id_access_token is not supplied, force usage of v1
124+
if id_access_token is None:
125+
use_v2 = False
79126

80-
if "client_secret" in creds:
81-
client_secret = creds["client_secret"]
82-
elif "clientSecret" in creds:
83-
client_secret = creds["clientSecret"]
127+
query_params = {"sid": creds["sid"], "client_secret": client_secret}
128+
129+
# Decide which API endpoint URLs and query parameters to use
130+
if use_v2:
131+
url = "https://%s%s" % (
132+
id_server,
133+
"/_matrix/identity/v2/3pid/getValidated3pid",
134+
)
135+
query_params["id_access_token"] = id_access_token
84136
else:
85-
raise SynapseError(400, "No client_secret in creds")
137+
url = "https://%s%s" % (
138+
id_server,
139+
"/_matrix/identity/api/v1/3pid/getValidated3pid",
140+
)
86141

87142
if not self._should_trust_id_server(id_server):
88143
logger.warn(
@@ -95,37 +150,44 @@ def threepid_from_creds(self, creds):
95150
if id_server in self.rewrite_identity_server_urls:
96151
id_server = self.rewrite_identity_server_urls[id_server]
97152
try:
98-
data = yield self.http_client.get_json(
99-
"https://%s%s"
100-
% (id_server, "/_matrix/identity/api/v1/3pid/getValidated3pid"),
101-
{"sid": creds["sid"], "client_secret": client_secret},
102-
)
153+
data = yield self.http_client.get_json(url, query_params)
154+
return data if "medium" in data else None
103155
except HttpResponseException as e:
104-
logger.info("getValidated3pid failed with Matrix error: %r", e)
105-
raise e.to_synapse_error()
156+
if e.code != 404 or not use_v2:
157+
# Generic failure
158+
logger.info("getValidated3pid failed with Matrix error: %r", e)
159+
raise e.to_synapse_error()
106160

107-
if "medium" in data:
108-
return data
109-
return None
161+
# This identity server is too old to understand Identity Service API v2
162+
# Attempt v1 endpoint
163+
logger.info("Got 404 when POSTing JSON %s, falling back to v1 URL", url)
164+
return (yield self.threepid_from_creds(creds, use_v2=False))
110165

111166
@defer.inlineCallbacks
112-
def bind_threepid(self, creds, mxid):
167+
def bind_threepid(self, creds, mxid, use_v2=True):
168+
"""Bind a 3PID to an identity server
169+
170+
Args:
171+
creds (dict[str, str]): Dictionary of credentials that contain the following keys:
172+
* client_secret|clientSecret: A unique secret str provided by the client
173+
* id_server|idServer: the domain of the identity server to query
174+
* id_access_token: The access token to authenticate to the identity
175+
server with. Required if use_v2 is true
176+
mxid (str): The MXID to bind the 3PID to
177+
use_v2 (bool): Whether to use v2 Identity Service API endpoints
178+
179+
Returns:
180+
Deferred[dict]: The response from the identity server
181+
"""
113182
logger.debug("binding threepid %r to %s", creds, mxid)
114-
data = None
115183

116-
if "id_server" in creds:
117-
id_server = creds["id_server"]
118-
elif "idServer" in creds:
119-
id_server = creds["idServer"]
120-
else:
121-
raise SynapseError(400, "No id_server in creds")
184+
client_secret, id_server, id_access_token = self._extract_items_from_creds_dict(
185+
creds
186+
)
122187

123-
if "client_secret" in creds:
124-
client_secret = creds["client_secret"]
125-
elif "clientSecret" in creds:
126-
client_secret = creds["clientSecret"]
127-
else:
128-
raise SynapseError(400, "No client_secret in creds")
188+
# If an id_access_token is not supplied, force usage of v1
189+
if id_access_token is None:
190+
use_v2 = False
129191

130192
# if we have a rewrite rule set for the identity server,
131193
# apply it now, but only for sending the request (not
@@ -135,11 +197,16 @@ def bind_threepid(self, creds, mxid):
135197
else:
136198
id_server_host = id_server
137199

200+
# Decide which API endpoint URLs to use
201+
bind_data = {"sid": creds["sid"], "client_secret": client_secret, "mxid": mxid}
202+
if use_v2:
203+
bind_url = "https://%s/_matrix/identity/v2/3pid/bind" % (id_server_host,)
204+
bind_data["id_access_token"] = id_access_token
205+
else:
206+
bind_url = "https://%s/_matrix/identity/api/v1/3pid/bind" % (id_server_host,)
207+
138208
try:
139-
data = yield self.http_client.post_json_get_json(
140-
"https://%s%s" % (id_server_host, "/_matrix/identity/api/v1/3pid/bind"),
141-
{"sid": creds["sid"], "client_secret": client_secret, "mxid": mxid},
142-
)
209+
data = yield self.http_client.post_json_get_json(bind_url, bind_data)
143210
logger.debug("bound threepid %r to %s", creds, mxid)
144211

145212
# Remember where we bound the threepid
@@ -149,9 +216,18 @@ def bind_threepid(self, creds, mxid):
149216
address=data["address"],
150217
id_server=id_server,
151218
)
219+
220+
return data
221+
except HttpResponseException as e:
222+
if e.code != 404 or not use_v2:
223+
logger.error("3PID bind failed with Matrix error: %r", e)
224+
raise e.to_synapse_error()
152225
except CodeMessageException as e:
153226
data = json.loads(e.msg) # XXX WAT?
154-
return data
227+
return data
228+
229+
logger.info("Got 404 when POSTing JSON %s, falling back to v1 URL", bind_url)
230+
return (yield self.bind_threepid(creds, mxid, use_v2=False))
155231

156232
@defer.inlineCallbacks
157233
def try_unbind_threepid(self, mxid, threepid):
@@ -207,6 +283,8 @@ def try_unbind_threepid_with_id_server(self, mxid, threepid, id_server):
207283
server doesn't support unbinding
208284
"""
209285
url = "https://%s/_matrix/identity/api/v1/3pid/unbind" % (id_server,)
286+
url_bytes = "/_matrix/identity/api/v1/3pid/unbind".encode("ascii")
287+
210288
content = {
211289
"mxid": mxid,
212290
"threepid": {"medium": threepid["medium"], "address": threepid["address"]},
@@ -218,7 +296,7 @@ def try_unbind_threepid_with_id_server(self, mxid, threepid, id_server):
218296
auth_headers = self.federation_http_client.build_auth_headers(
219297
destination=None,
220298
method="POST",
221-
url_bytes="/_matrix/identity/api/v1/3pid/unbind".encode("ascii"),
299+
url_bytes=url_bytes,
222300
content=content,
223301
destination_is=id_server,
224302
)

synapse/rest/client/v2_alpha/account.py

+7-5
Original file line numberDiff line numberDiff line change
@@ -589,12 +589,14 @@ def on_POST(self, request):
589589

590590
# skip validation if this is a shadow 3PID from an AS
591591
if not requester.app_service:
592-
threePidCreds = body.get("threePidCreds")
593-
threePidCreds = body.get("three_pid_creds", threePidCreds)
594-
if threePidCreds is None:
592+
threepid_creds = body.get("threePidCreds") or body.get("three_pid_creds")
593+
if threepid_creds is None:
595594
raise SynapseError(400, "Missing param", Codes.MISSING_PARAM)
596595

597-
threepid = yield self.identity_handler.threepid_from_creds(threePidCreds)
596+
requester = yield self.auth.get_user_by_req(request)
597+
user_id = requester.user.to_string()
598+
599+
threepid = yield self.identity_handler.threepid_from_creds(threepid_creds)
598600

599601
if not threepid:
600602
raise SynapseError(
@@ -618,7 +620,7 @@ def on_POST(self, request):
618620

619621
if not requester.app_service and ("bind" in body and body["bind"]):
620622
logger.debug("Binding threepid %s to %s", threepid, user_id)
621-
yield self.identity_handler.bind_threepid(threePidCreds, user_id)
623+
yield self.identity_handler.bind_threepid(threepid_creds, user_id)
622624

623625
if self.hs.config.shadow_server:
624626
shadow_user = UserID(

0 commit comments

Comments
 (0)