Support DigestAuth with "x-www-authenticate" in first response header
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 1.5k
- Forks
- 78
- Avg merge
- 8h 59m
- Merged PRs (30d)
- 24
Description
Originally opened by
@kiryphon 2024-09-19 14:21:02 in encode/httpx
The Fronius inverter for photovoltaic systems uses for its local REST API digest authentication.
It uses its initial response header "X-WWW-Authenticate" instead of "www-authenticate" and hence make the httpx request fail.
Here an initial curl command with the response header:
❯ curl -I "http://192.168.1.141/config/emrs"
HTTP/1.1 401 Unauthorized
X-WWW-Authenticate: Digest realm="Webinterface area", charset="UTF-8", algorithm=MD5, nonce="66ec2f8d:36a65537553cf788606762854336a0b3", qop="auth"
Content-Type: text/html
Content-Length: 347
Date: Thu, 19 Sep 2024 14:05:01 GMT
Server: webserver
httpx 0.27.2 fails to extract X-WWW-Authenticate with nonce, qop, algorithm, realm in
My script
import httpx
url = "http://192.168.1.141/config/emrs" # adjust IP to that one of your inverter
user = "technician"
pw = <INSERT PW>
auth = httpx.DigestAuth(user,pw)
with httpx.Client(auth=auth) as client:
r = client.get(url)
print(r.status_code)
print(r.json()['priorities'])
r = client.get(url)
print(r.status_code)
print(r.json()['priorities'])
runs only with following quick diff
❯ git diff
diff --git a/httpx/_auth.py b/httpx/_auth.py
index b03971a..fb6ad69 100644
--- a/httpx/_auth.py
+++ b/httpx/_auth.py
@@ -198,12 +198,22 @@ class DigestAuth(Auth):
response = yield request
- if response.status_code != 401 or "www-authenticate" not in response.headers:
+ auth_strings = [ "www-authenticate", "x-www-authenticate" ]
+ auth_string = False
+ for a in auth_strings:
+ if a in response.headers:
+ if auth_string == False:
+ auth_string = a
+ else:
+ message = "Malformed Digest WWW-Authenticate response header"
+ raise ProtocolError(message, request=request)
+
+ if response.status_code != 401 or auth_string == False:
# If the response is not a 401 then we don't
# need to build an authenticated request.
return
- for auth_header in response.headers.get_list("www-authenticate"):
+ for auth_header in response.headers.get_list(auth_string):
if auth_header.lower().startswith("digest "):
break
else:
Note, this diff is only to show to me that it works. I did not follow any coding guidelines of this project.
For reference, here a couple of projects which have written their custom digest authorization to handle the Fronius reponse header using the request library:
- https://github.com/wiggal/GEN24_Ladesteuerung/blob/main/FUNCTIONS/httprequest.py
- https://github.com/muexxl/batcontrol/blob/main/fronius/fronius.py#L326-L361
- https://github.com/jakkaj/ha_fronius_set_service/blob/main/fronius_settings_service/script.py#L28-L112
- https://github.com/sergioperez/fronius-auth-proxy/blob/master/makeRequest.js
I am not sure how common it is to use X-WWW-Authenticate. Google did not return many results:
However, I still think supporting this deviation might be acceptable. If Fronius screwed this up more heavily than I think, I understand that httpx cannot support all quirks of all digest implementations.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in httpx/_auth.py at the DigestAuth response handling referenced by the issue, then reproduce the supplied Python example or inspect the shown 401 response header. Done means DigestAuth can extract a Digest challenge from X-WWW-Authenticate as well as the standard www-authenticate header without breaking the existing flow.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- authentication
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 72/100