-
-
Notifications
You must be signed in to change notification settings - Fork 861
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Handle multiple auth headers correctly (#1240)
Handle multiple auth headers correctly
- Loading branch information
1 parent
fa7661b
commit 33d339a
Showing
2 changed files
with
29 additions
and
14 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -112,28 +112,34 @@ def auth_flow(self, request: Request) -> typing.Generator[Request, Response, Non | |
response = yield request | ||
|
||
if response.status_code != 401 or "www-authenticate" not in response.headers: | ||
# If the response is not a 401 WWW-Authenticate, then we don't | ||
# If the response is not a 401 then we don't | ||
# need to build an authenticated request. | ||
return | ||
|
||
challenge = self._parse_challenge(request, response) | ||
for auth_header in response.headers.get_list("www-authenticate"): | ||
if auth_header.lower().startswith("digest "): | ||
break | ||
else: | ||
# If the response does not include a 'WWW-Authenticate: Digest ...' | ||
# header, then we don't need to build an authenticated request. | ||
return | ||
|
||
challenge = self._parse_challenge(request, response, auth_header) | ||
request.headers["Authorization"] = self._build_auth_header(request, challenge) | ||
yield request | ||
|
||
def _parse_challenge( | ||
self, request: Request, response: Response | ||
self, request: Request, response: Response, auth_header: str | ||
) -> "_DigestAuthChallenge": | ||
""" | ||
Returns a challenge from a Digest WWW-Authenticate header. | ||
These take the form of: | ||
`Digest realm="[email protected]",qop="auth,auth-int",nonce="abc",opaque="xyz"` | ||
""" | ||
header = response.headers["www-authenticate"] | ||
scheme, _, fields = auth_header.partition(" ") | ||
|
||
scheme, _, fields = header.partition(" ") | ||
if scheme.lower() != "digest": | ||
message = "Header does not start with 'Digest'" | ||
raise ProtocolError(message, request=request) | ||
# This method should only ever have been called with a Digest auth header. | ||
assert scheme.lower() == "digest" | ||
|
||
header_dict: typing.Dict[str, str] = {} | ||
for field in parse_http_list(fields): | ||
|
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -356,6 +356,21 @@ async def test_digest_auth_returns_no_auth_if_no_digest_header_in_response() -> | |
assert len(response.history) == 0 | ||
|
||
|
||
def test_digest_auth_returns_no_auth_if_alternate_auth_scheme() -> None: | ||
url = "https://example.org/" | ||
auth = DigestAuth(username="tomchristie", password="password123") | ||
auth_header = b"Token ..." | ||
|
||
client = httpx.Client( | ||
transport=SyncMockTransport(auth_header=auth_header, status_code=401) | ||
) | ||
response = client.get(url, auth=auth) | ||
|
||
assert response.status_code == 401 | ||
assert response.json() == {"auth": None} | ||
assert len(response.history) == 0 | ||
|
||
|
||
@pytest.mark.asyncio | ||
async def test_digest_auth_200_response_including_digest_auth_header() -> None: | ||
url = "https://example.org/" | ||
|
@@ -519,9 +534,6 @@ async def test_digest_auth_incorrect_credentials() -> None: | |
"auth_header", | ||
[ | ||
b'Digest realm="[email protected]", qop="auth"', # missing fields | ||
b'realm="[email protected]", qop="auth"', # not starting with Digest | ||
b'DigestZ realm="[email protected]", qop="auth"' | ||
b'qop="auth,auth-int",nonce="abc",opaque="xyz"', | ||
b'Digest realm="[email protected]", qop="auth,au', # malformed fields list | ||
], | ||
) | ||
|
@@ -542,9 +554,6 @@ async def test_async_digest_auth_raises_protocol_error_on_malformed_header( | |
"auth_header", | ||
[ | ||
b'Digest realm="[email protected]", qop="auth"', # missing fields | ||
b'realm="[email protected]", qop="auth"', # not starting with Digest | ||
b'DigestZ realm="[email protected]", qop="auth"' | ||
b'qop="auth,auth-int",nonce="abc",opaque="xyz"', | ||
b'Digest realm="[email protected]", qop="auth,au', # malformed fields list | ||
], | ||
) | ||
|