-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Support registration & login with phone number #1971
Conversation
dbkr
commented
Mar 7, 2017
- Adds the msisdn form of the proxy requestToken APIs
- Changes the call to the ID servers to use JSON rather than x-www-form-urlencoded
- Adds support for binding msisdns at registration time
- Adds support for m.login.msisdn UI auth type and offers flows that use it in the registration API
defer.returnValue doth not maketh a generator: it would need a yield to be a generator, and this doesn't need a yield.
and support binding them with the bind_msisdn param
FTR: this implements https://docs.google.com/document/d/1-6ZSSW5YvCGhVFDyD2QExAUAdpCWjccvJT5xiyTTG2Y/edit |
synapse/handlers/auth.py
Outdated
defer.returnValue(True) | ||
|
||
@defer.inlineCallbacks | ||
def _check_threepid(self, medium, authdict, ): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
random trailing ,
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
synapse/rest/client/v1/login.py
Outdated
del submission["medium"] | ||
del submission["address"] | ||
|
||
return submission |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since you're modifying the input, I'd suggest not returning it, to make that clear.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
synapse/rest/client/v1/login.py
Outdated
if "type" not in identifier: | ||
raise SynapseError(400, "Login identifier has no type") | ||
|
||
# convert phone type identifiers to geberic threepids |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
geberic
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
raise SynapseError(400, "Unable to parse phone number") | ||
msisdn = phonenumbers.format_number( | ||
phoneNumber, phonenumbers.PhoneNumberFormat.E164 | ||
)[1:] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's factor out the (phone_number, country) -> msisdn_or_throw_400
conversion to a utility, since it appears in several places.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
if existingUid is None: | ||
raise SynapseError(400, "MSISDN not found", Codes.THREEPID_NOT_FOUND) | ||
|
||
ret = yield self.identity_handler.requestEmailToken(**body) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should be requestMsisdnToken?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
absent.append(k) | ||
|
||
if len(absent) > 0: | ||
raise SynapseError(400, "Missing params: %r" % absent, Codes.MISSING_PARAM) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could we not have an assert_params_in_request(body, required)
utility rather than copying this pattern twenty times?
suggest sticking it alongside parse_json_object_from_request
in servlet.py
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
@@ -226,6 +281,7 @@ def on_POST(self, request): | |||
) | |||
# don't re-register the email address |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/email address/threepids/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
@@ -372,6 +436,44 @@ def _register_email_threepid(self, user_id, threepid, token, bind_email): | |||
logger.info("bind_email not specified: not binding email") | |||
|
|||
@defer.inlineCallbacks | |||
def _register_msisdn_threepid(self, user_id, threepid, token, bind_msisdn): | |||
"""Add aphone number as a 3pid identifier |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
aphone
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
""" | ||
reqd = ('medium', 'address', 'validated_at') | ||
if any(x not in threepid for x in reqd): | ||
logger.info("Can't add incomplete 3pid") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when would this happen?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would have to be the ID server giving an invalid response
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
comment would help, then
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've only looked at the synapse/python part of it, not the functionality. Looks mostly good, other than factoring a few bits out to reduce code duplication
@defer.inlineCallbacks | ||
def _check_dummy_auth(self, authdict, _): | ||
yield run_on_reactor() | ||
defer.returnValue(True) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we do wait a reactor tick?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because these functions are all specified to return a deferred, so we need to yield somewhere in the function for it to actually return a deferred.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you could do that with return success(True)
.
A better answer might be: because it did before we got here, and changing it is scary.
synapse/handlers/auth.py
Outdated
yield run_on_reactor() | ||
|
||
if 'threepid_creds' not in authdict: | ||
raise LoginError(400, "Missing threepid_creds", Codes.MISSING_PARAM) | ||
|
||
threepid_creds = authdict['threepid_creds'] | ||
|
||
identity_handler = self.hs.get_handlers().identity_handler | ||
|
||
logger.info("Getting validated threepid. threepidcreds: %r" % (threepid_creds,)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know this isn't you, but this should be logger.info("..", threepid_creds)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It probably was me in a former life
synapse/rest/client/v1/login.py
Outdated
""" | ||
Convert a phone login identifier type to a generic threepid identifier | ||
Args: | ||
identifier: Login identifier dict of type 'm.id.phone' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you're listing args please add type, e.g. identifier(str): ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
absent.append(k) | ||
|
||
if absent: | ||
raise SynapseError(400, "Missing params: %r" % absent, Codes.MISSING_PARAM) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This checking of required keys should probably be factored out as its shared between at least this and the email one
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
raise SynapseError(400, "Unable to parse phone number") | ||
msisdn = phonenumbers.format_number( | ||
phoneNumber, phonenumbers.PhoneNumberFormat.E164 | ||
)[1:] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this the same block of code as in login_id_thirdparty_from_phone
? Its probably worth factoring out as a) DRY and b) it'll probably be easier to see whats going on in this function
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
phoneNumber, phonenumbers.PhoneNumberFormat.E164 | ||
)[1:] | ||
|
||
existingUid = yield self.hs.get_datastore().get_user_id_by_threepid( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should be avoiding using self.hs.get_*()
outside of initialisers and instead pull out all the dependencies from the HS we need in the initialiser
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
reqd = ('medium', 'address', 'validated_at') | ||
if any(x not in threepid for x in reqd): | ||
logger.info("Can't add incomplete 3pid") | ||
defer.returnValue() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will likely fail as it doesn't have a parameter? If you just want to return None you can actually just do return
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you? really? I thought it was naughty to have yield
and return
in the same function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah, you can return
without a value.
logger.info("bind_msisdn specified: binding") | ||
logger.debug("Binding msisdn %s to %s" % ( | ||
threepid, user_id | ||
)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the reasoning for not just having this as one info log line?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm, it probably used to be longer. fixed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assumed it was something to do with not wanting to put the email/msisdn in the log.
raise SynapseError(400, "Unable to parse phone number") | ||
msisdn = phonenumbers.format_number( | ||
phoneNumber, phonenumbers.PhoneNumberFormat.E164 | ||
)[1:] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, this would be much nicer to read if factored out a bit:
def on_POST(self, request):
body = parse_json_object_from_request(request)
check_required_keys(request, ('id_server', 'client_secret', ..))
msisdn = format_msisdn(body['phone_number'], body['country'])
existingUid = yield self.store.get_user_id_by_threepid('msisdn', msisdn)
if existingUid:
raise
ret = yield self.identity_handler.requestEmailToken(**body)
defer.returnValue((200, ret))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
Plus a couple of other minor fixes
and replace requestEmailToken where we meant requestMsisdnToken
@erikjohnston @richvdh ptal |
synapse/rest/client/v1/login.py
Outdated
If the input login submission is an old style object | ||
(ie. with top-level user / medium / address) convert it | ||
to a typed object. | ||
Returns: Typed-object style login identifier |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LIES!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah yes
synapse/util/msisdn.py
Outdated
@@ -0,0 +1,28 @@ | |||
# -*- coding: utf-8 -*- | |||
# Copyright 2015, 2016 OpenMarket Ltd |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
technically not
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
from synapse.api.errors import SynapseError | ||
|
||
|
||
def phone_number_to_msisdn(country, number): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can haz doc pls kthxbye
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
u haz doc
""" | ||
reqd = ('medium', 'address', 'validated_at') | ||
if any(x not in threepid for x in reqd): | ||
logger.info("Can't add incomplete 3pid") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
comment would help, then
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm