A contact channel validates itself, and says what would confirm it #146
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Blocks
#142 A number nobody verified cannot get anybody back in
Postulo/postulo
#147 An address is checked against the rules of its own country
Postulo/postulo
Reference
Postulo/postulo#146
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Observation
What exists
Three kinds of contact detail, at three different stages, each having invented its own
answer:
EmailAddress)PhoneNumber, #90)Validation is deliberately shallow for telephone numbers, and the reason expires.
phones.pysays it outright:That was true when a number was something a person read off a screen and typed into a
handset. The moment a number is a channel Postulo sends a code to, Postulo is dialling it,
and the argument for not checking the shape goes with it.
Confirmation exists once, for email, and is not reusable. allauth owns the flow: the
token, the expiry, the resend, the link, the
verifiedflag. Nothing in Postulo can ask atransport to carry a confirmation for something that is not an email address.
What this asks for
One contract that every kind of contact detail obeys: how it is shaped, whether it can be
proved, and which connector proves it.
Worth being careful about
Not everything confirmable is confirmable the same way, and one of them is not confirmable
at all. An address takes a link; a number takes a short code typed back, because a link in
an SMS is a phishing lesson nobody should be teaching. A postal address can only be confirmed
by posting something to it, which is what some services do and is not a thing this project
will build — so the contract has to allow validated but not confirmable as a legitimate
answer, rather than treating it as a missing feature.
Validation has three depths and they should be named. Syntactic (is this the shape of an
address, does this parse as E.164); plausible (is this dialling code assigned, does this
domain have an MX record); and real (does anybody answer). Postulo currently does the first
for email through allauth and less than the first for numbers. The middle one is where a
library and a data file arrive, which is the cost
phones.pyrefused; it is worth refusingagain, deliberately, rather than by inheritance.
A confirmation is a message, so it needs the thing that carries messages. For email that
is the transport, which already exists and is already locked when it is the last way back in
(#104). For a number it is whatever #143 decides. The contract should ask a channel which
connector confirms me and get a name back, rather than each kind reaching for its own.
Rate limits belong in the contract, not in each implementation. A resend button is a way
to make somebody's phone buzz forty times, and one of these channels costs money per send.
POSTULO_CAPTURE_RATEand its siblings are the precedent.Every message is a message somebody reads in their own language, and a confirmation code
sent to a number has no interface around it to carry context — so its text has to say which
instance sent it and what it is for, in the recipient's language, in one line.
allauth is not going to be rewritten to fit this. The contract has to be one an existing,
external, working implementation can be described by rather than one it must adopt: email
satisfies the contract by delegation, and the two new kinds implement it directly. A contract
that requires changing allauth is a contract that will not be adopted.
This is the shape of a plugin kind, and it inherits their rules. If a contact channel is
a plugin (#90 made the first of them one), then #126's surface, #128's data question and
#129's self-containment all apply, and so does the rule that switching one off may not remove
somebody's last way back into their account.
postulo.core.channelsholds the contract: aContactChannelprotocol with acheck()that reports shape, a named depth, aproofkind, and acarrier()naming whatwould carry a confirmation.
The three depths are named and the middle one is refused again, deliberately rather than
by inheritance. The issue was right that half of
phones.py's argument expires when a numberbecomes a channel Postulo sends to — and only half: the cost of a numbering-plan library is
unchanged, and the thing that settles the question is the confirmation itself, which is free
and conclusive. A library saying a range is assigned would sit between a typed value and a
check that answers it anyway.
What did change: telephone checking rises from less than syntactic to syntactic — a
country in front, four to fifteen digits, which is E.164 and costs nothing. A number that
fails is still saved.
phones.pymeans what it says; this reports, and the caller thatrefuses is #142.
Validated-but-never-provable is expressible, and kept apart from a second empty answer:
UNPROVABLE(postal, permanently) versus a channel that is provable and has no carrier yet(telephone, until #143). Somebody locked out of their account deserves to be told which of
the two they met, and #144 now has the vocabulary for it.
allauth is described, not rewritten.
EmailChanneldelegates: allauth keeps the token,the expiry, the resend, the link and the verified flag. Its
carrier()returns whatevertransport is selected rather than assuming SMTP, so it and the #104 interlock read the same
answer.
Rate limits are in the contract, as asked:
POSTULO_CONFIRMATION_RATE, one limit acrossevery channel, because the channel that costs money per send is not the one whose own setting
anybody would remember to configure. And
one_line()builds the message a code arrives in —naming the instance and saying nobody will ask for it, because a stranger's message containing
six digits is the shape of every scam there is.
Not a plugin kind yet, and the reason is recorded. A plugin can be switched off, and a
channel that can be switched off is somebody's way back into their account that can be switched
off. Transports have an interlock for exactly that (#104), and it would need extending before
the two ideas could safely meet. The protocol is the one a kind would declare, so #128 and
#129 apply to the step rather than to a redesign.
A
Postalstand-in lives in the tests rather than in the codebase — a channel for a modelPostulo does not have yet would be speculation, and #147 brings the real one.
27 tests in
tests/test_contact_channels.py, a wiki section under Configuration, and sixstrings in all 39 European catalogues.
Shipped in
019d7c8on0.3.0, withmainkept level.