-
-
Notifications
You must be signed in to change notification settings - Fork 841
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for (auth-int) quality of protection #2605
Conversation
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.
Seems okay to me yup, thank for this!
Needs a test case adding to it.
tests/client/test_auth.py
Outdated
@pytest.mark.anyio | ||
async def test_digest_auth_qop_auth_int_not_implemented() -> None: | ||
url = "https://example.org/" | ||
auth = httpx.DigestAuth(username="user", password="password123") | ||
app = DigestApp(qop="auth-int") | ||
|
||
async with httpx.AsyncClient(transport=httpx.MockTransport(app)) as client: | ||
with pytest.raises(NotImplementedError): | ||
await client.get(url, auth=auth) | ||
|
||
|
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.
Instead of removing this test case, let's change it so that we're testing auth-int
.
Great. This looks good. The trade-off that we're making if we pull this in, is that digest auth will then always pre-emptively read the response body. So... no streaming responses if you're using digest auth. Even if it's not an Is that a trade-off we want to make in order to add this support? |
I considered it, and I believe we can solve this problem in the future, but for now, I think 'auth-int' support is more important than stream reading, which is rarely used in my opinion. I decided to make this pull request because I used httpx in my application, which sends requests to many servers, and some of them required 'auth-int' authorization, which httpx did not support. |
Also, I believe we can solve this problem by overriding |
True. To do that with a reasonably minimal code change, I think we could do something like this... requires_request_body = False
def sync_auth_flow(...):
if auth-int in headers
self.requires_request_body = True
return super().sync_auth_flow(...)
async def async_auth_flow(...):
if auth-int in headers
self.requires_request_body = True
return await super().async_auth_flow(...) ...so that we're not having to override the entire methods.
I'm not all that keen on the extra complexity, but the above looks like it might be simple enough to be worth the trade off. What are your thoughts on it? |
I believe the only way to override is to copy the entire Unfortunately, we do not have easy access to that response, and we cannot do so without coping whole methods. 1 def sync_auth_flow(
2 self, request: Request
3 ) -> typing.Generator[Request, Response, None]:
4 """
5 Execute the authentication flow synchronously.
6
7 By default, this defers to `.auth_flow()`. You should override this method
8 when the authentication scheme does I/O and/or uses concurrency primitives.
9 """
10
11 if self.requires_request_body:
12 request.read()
13
14 flow = self.auth_flow(request)
15 request = next(flow)
16
17 while True:
18 response = yield request
19 if self.requires_response_body:
20 response.read()
21
22 try:
23 request = flow.send(response)
24 except StopIteration:
25 break We can either copy the entire function and change the 19th line, or we can go another way and check for 'auth-int' in the 'Auth.sync auth flow' and 'Auth.async auth flow' like so. class Auth:
...
def sync_auth_flow(
if self.requires_request_body:
request.read()
flow = self.auth_flow(request)
request = next(flow)
is_digest = isinstance(self, DigestAuth)
while True:
response = yield request
if self.requires_response_body or (
is_digest
and "www-authenticate" in response.headers
and "auth-int" in response.headers["www-authenticate"]
):
response.read()
try:
request = flow.send(response)
except StopIteration:
break We have a weird |
I see what you mean, yep. Okay, the additional support of |
That appears to be all. |
Ah right, I've just reviewed and seen commit d846089. |
Did you think we should override entire auth_flow methods, or do you refuse to support auth-int at all? |
Overriding auth_flows is a bad idea in my opinion; there will be four methods with the same body in the |
I think we either don't support it or we do support it, but just using a plain (In other words, roll back the d846089 commit) |
Sounds good |
@tomchristie Could you please merge this pull request? |
Okay... I've been a little slow on this because Ive been weighing it up as a trade-off. Thanks for being patient.
Could you show an example of how the requests/responses look with that once we've got this PR in? |
import httpx
auth = httpx.DigestAuth(username="test", password="test")
resp = httpx.get("https://httpbin.org/digest-auth/auth-int/test/test", auth=auth, timeout=1123)
print(resp)
print(resp.history[0].headers) OUTPUT
|
@tomchristie There is a strange issue, but I believe it is a httpbin fault; I tried using original httpx to do this request; I just hardcoded that qop is always equal to auth to avoid the DigestAuth not supported error, but this request still succeeds. |
@tomchristie It appears that httpcore is accepting In the original httpx, I modified those parts of code for local tests. Lines 246 to 248 in 472597f
Lines 293 to 294 in 472597f
This works correct [200] return _DigestAuthChallenge(
realm=realm, nonce=nonce, algorithm=algorithm, opaque=opaque, qop=b"auth"
) if qop:
format_args["qop"] = b"auth" This fails [401] return _DigestAuthChallenge(
realm=realm, nonce=nonce, algorithm=algorithm, opaque=opaque, qop=b"auth"
) if qop:
format_args["qop"] = b"auth-int" Httpsore simply sees that we sent the request as a normal qop=auth and does us a favor by accepting such a header. |
Okay, I'm going to punt this out to the team... Here's the trade-off: +ve: We add I think our options here are either...
Useful signposts we could follow here: does |
Curl supports |
I forced pushed into my httpx master to sync it with the real master, but I forgot I had a pull request in my master, so it broke. If we decide we really want this PR, I will repair it and open the same pull request. |
Okay thanks, I'd suggest we leave this one as-is unless we've got some good motivation otherwise. |
For tests, I used httpbin.