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 |
| defer.returnValue(True) | ||
|
|
||
| @defer.inlineCallbacks | ||
| def _check_threepid(self, medium, authdict, ): |
| del submission["medium"] | ||
| del submission["address"] | ||
|
|
||
| return submission |
There was a problem hiding this comment.
since you're modifying the input, I'd suggest not returning it, to make that clear.
| if "type" not in identifier: | ||
| raise SynapseError(400, "Login identifier has no type") | ||
|
|
||
| # convert phone type identifiers to geberic threepids |
| raise SynapseError(400, "Unable to parse phone number") | ||
| msisdn = phonenumbers.format_number( | ||
| phoneNumber, phonenumbers.PhoneNumberFormat.E164 | ||
| )[1:] |
There was a problem hiding this comment.
let's factor out the (phone_number, country) -> msisdn_or_throw_400 conversion to a utility, since it appears in several places.
| if existingUid is None: | ||
| raise SynapseError(400, "MSISDN not found", Codes.THREEPID_NOT_FOUND) | ||
|
|
||
| ret = yield self.identity_handler.requestEmailToken(**body) |
| absent.append(k) | ||
|
|
||
| if len(absent) > 0: | ||
| raise SynapseError(400, "Missing params: %r" % absent, Codes.MISSING_PARAM) |
There was a problem hiding this comment.
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
| @@ -226,6 +281,7 @@ def on_POST(self, request): | |||
| ) | |||
| # don't re-register the email address | |||
| logger.info("bind_email not specified: not binding email") | ||
|
|
||
| @defer.inlineCallbacks | ||
| def _register_msisdn_threepid(self, user_id, threepid, token, bind_msisdn): |
| defer.Deferred: | ||
| """ | ||
| reqd = ('medium', 'address', 'validated_at') | ||
| if any(x not in threepid for x in reqd): |
There was a problem hiding this comment.
This would have to be the ID server giving an invalid response
erikjohnston
left a comment
There was a problem hiding this comment.
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.
Why do we do wait a reactor tick?
There was a problem hiding this comment.
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.
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.
|
|
||
| identity_handler = self.hs.get_handlers().identity_handler | ||
|
|
||
| logger.info("Getting validated threepid. threepidcreds: %r" % (threepid_creds,)) |
There was a problem hiding this comment.
I know this isn't you, but this should be logger.info("..", threepid_creds)
There was a problem hiding this comment.
It probably was me in a former life
| """ | ||
| 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.
If you're listing args please add type, e.g. identifier(str): ...
| absent.append(k) | ||
|
|
||
| if absent: | ||
| raise SynapseError(400, "Missing params: %r" % absent, Codes.MISSING_PARAM) |
There was a problem hiding this comment.
This checking of required keys should probably be factored out as its shared between at least this and the email one
| raise SynapseError(400, "Unable to parse phone number") | ||
| msisdn = phonenumbers.format_number( | ||
| phoneNumber, phonenumbers.PhoneNumberFormat.E164 | ||
| )[1:] |
There was a problem hiding this comment.
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
| phoneNumber, phonenumbers.PhoneNumberFormat.E164 | ||
| )[1:] | ||
|
|
||
| existingUid = yield self.hs.get_datastore().get_user_id_by_threepid( |
There was a problem hiding this comment.
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
| """ | ||
| 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.
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.
can you? really? I thought it was naughty to have yield and return in the same function.
There was a problem hiding this comment.
ah, you can return without a value.
| if bind_msisdn: | ||
| logger.info("bind_msisdn specified: binding") | ||
| logger.debug("Binding msisdn %s to %s" % ( | ||
| threepid, user_id |
There was a problem hiding this comment.
What's the reasoning for not just having this as one info log line?
There was a problem hiding this comment.
hmm, it probably used to be longer. fixed.
There was a problem hiding this comment.
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.
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))Plus a couple of other minor fixes
and replace requestEmailToken where we meant requestMsisdnToken
|
@erikjohnston @richvdh ptal |
| 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 |
| @@ -0,0 +1,28 @@ | |||
| # -*- coding: utf-8 -*- | |||
| # Copyright 2015, 2016 OpenMarket Ltd | |||
| from synapse.api.errors import SynapseError | ||
|
|
||
|
|
||
| def phone_number_to_msisdn(country, number): |
| defer.Deferred: | ||
| """ | ||
| reqd = ('medium', 'address', 'validated_at') | ||
| if any(x not in threepid for x in reqd): |