Add HTTP Basic auth as an alternative to OAuth - #109
Merged
Conversation
Every endpoint accepts the account's email and password in an ordinary
Authorization header, which sidesteps the token lifecycle entirely:
nothing expires, there is no consumer to register, and neither of the
per-IP rate limits on consumer_token and authorize applies. Verified
against meters, field_names, last_reading, readings, statistics,
devices, disaggregation and activities. The 401 for a missing
credential advertises the scheme itself:
www-authenticate: Basic realm="Discovergy", charset="UTF-8"
The API documents none of this, so basic_auth/3 is an alternative to
login/3 rather than a replacement, and login/3 stays the documented way
in. A wrong password comes back as a 401 that says so, unlike the
empty-bodied 401 of an expired token, so the two never need telling
apart. Nothing goes out when the client is built, so basic_auth/3
returns the client rather than {:ok, client}.
The :consumer and :token fields collapse into a single :credentials
field holding either an OAuth session or the Basic auth pair. Only two
of the four combinations were ever legal, and the request path now
dispatches on one value instead of picking its way through them. The
client no longer knows how either scheme builds a header:
OAuth.authorization/4 and BasicAuth.authorization/1 do that themselves,
both returning a lowercase name to match the rest of the request.
credentials/1 reads the session back for persistence, which the struct
being opaque had previously left to reaching into documented fields.
Reauthorizing a Basic auth client reports :not_logged_in, since there
is no OAuth session to renew.
It prescribed :erlang.term_to_binary/1 on the grounds that the structs "redact themselves when inspected and encode to no other format", which confuses two unrelated things: Inspect governs display, and term_to_binary never consults it. It was also wrong for the common case, since a term kept in ETS, a GenServer, :persistent_term or an Agent is never serialized at all, and the warning about blobs from older versions was generic struct advice rather than anything about this library. How a caller stores an opaque term is not the library's business. What is: an OAuth session saves two rate limited calls, and a Basic auth one is not worth storing at all.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Every endpoint accepts the account's email and password in an ordinary
Authorizationheader, which sidesteps the token lifecycle entirely: nothing expires, there is no consumer to register, and neither of the per-IP rate limits onconsumer_tokenandauthorizeapplies. That is most of what Quirks of the API warns about, gone.Verified against
meters,field_names,last_reading,readings,statistics,devices,disaggregationandactivities, all200. The401for a missing credential advertises the scheme itself, so this is not an accident of the routing layer:The API documents none of it, which is why this is an alternative to
login/3rather than a replacement.login/3stays the documented way in, and everything the guide says about tokens still applies whenever it is used.Nothing goes out when the client is built, so
basic_auth/3returns the client rather than{:ok, client}. A wrong password therefore surfaces on the first real request, as a401that says what is wrong:That body is the whole difference in error handling. An expired OAuth token is an empty-bodied
401worth recovering from; a401here is a credential that will not start working on its own.One auth field instead of two
:consumerand:tokencollapse into a single:credentialsfield holding either an OAuth session or the Basic auth pair. Only two of the four field combinations were ever legal, and the request path now dispatches on one value instead of picking its way through them.Clientalso stops knowing how either scheme builds a header:OAuth.authorization/4andBasicAuth.authorization/1do that themselves, both returning a lowercase header name to match the rest of the request.credentials/1reads the session back for persistence, which the struct being@opaquehad previously left to reaching into documented fields:Reauthorizing a Basic auth client reports
:not_logged_in, since there is no OAuth session to renew.Verification
Both paths exercised live against the public demo account, plus an OAuth session serialized and restored into a fresh client.