Skip to content

Commit

Permalink
cleanup headers logic
Browse files Browse the repository at this point in the history
  • Loading branch information
Colin-b committed Feb 11, 2024
1 parent 13c44a7 commit ffaddf3
Show file tree
Hide file tree
Showing 4 changed files with 16 additions and 21 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,7 @@ Note that the following changes were made compared to `requests-aws4auth`:
- `host` is not considered as a specific Amazon service anymore (no test specific code).
- Canonical query string computation is entirely based on AWS documentation (and consider undocumented fragment (`#` and following characters) as not part of the query string).
- Canonical uri computation is entirely based on AWS documentation.
- Canonical headers computation is almost entirely based on AWS documentation.

### Parameters

Expand Down
26 changes: 13 additions & 13 deletions httpx_auth/aws.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,9 @@ def __init__(

self.security_token = kwargs.get("security_token")

# https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html
# For the purpose of calculating an authorization signature, only the host and any x-amz-* headers are required;
# however, in order to prevent data tampering, you should consider including all the headers in the signature calculation.
self.include_headers = {
header.lower() for header in kwargs.get("include_headers", [])
}
self.include_headers.add("host")
self.include_headers.add("content-type")

def auth_flow(
self, request: httpx.Request
Expand Down Expand Up @@ -127,7 +122,7 @@ def _canonical_request(
def canonical_and_signed_headers(
headers: httpx.Headers, include_headers: set[str]
) -> tuple[str, str]:
"""
r"""
See https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html for more details.
CanonicalHeaders is a list of request headers with their values.
Expand Down Expand Up @@ -171,15 +166,19 @@ def canonical_and_signed_headers(
For example, for the previous example, the value of SignedHeaders would be as follows:
host;x-amz-content-sha256;x-amz-date
>>> canonical_and_signed_headers(httpx.Headers({"Host": "s3.amazonaws.com", "x-amz-content-sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "x-amz-date": "20130708T220855Z"}), include_headers={"host"})
('host:s3.amazonaws.com\nx-amz-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\nx-amz-date:20130708T220855Z\n', 'host;x-amz-content-sha256;x-amz-date')
"""
include_headers.add("host")
include_headers.add("content-type")
included_headers = {}
for header, header_value in headers.items():
if (header or "*") in include_headers or (
header.startswith("x-amz-")
# x-amz-client-context break mobile analytics auth if included
and not header == "x-amz-client-context"
):
included_headers[header] = _amz_norm_whitespace(header_value)
included_headers[header] = trim(header_value)

canonical_headers = ""
signed_headers = []
Expand Down Expand Up @@ -307,14 +306,15 @@ def sign_sha256(signing_key: bytes, message: str) -> bytes:
return hmac.new(signing_key, message.encode("utf-8"), hashlib.sha256).digest()


def _amz_norm_whitespace(text: str) -> str:
def trim(value: str) -> str:
"""
Replace runs of whitespace with a single space.
Ignore text enclosed in quotes.
>>> trim(" this is the value ")
'this is the value'
"""
if re.search(r"\s", text):
return " ".join(shlex.split(text, posix=False)).strip()
return text
# TODO AWS documentation expects only leading or trailing whitespace to be removed.
if re.search(r"\s", value):
return " ".join(shlex.split(value, posix=False)).strip()
return value


def uri_encode(value: str, is_key: bool = False) -> str:
Expand Down
5 changes: 1 addition & 4 deletions tests/aws_signature_v4/test_aws4auth_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,7 @@ async def test_aws_auth_share_security_tokens_between_instances(
service="iam",
security_token="security_token",
)
assert auth2.include_headers == {
"content-type",
"host",
}
assert auth2.include_headers == set()

httpx_mock.add_response(
url="https://authorized_only",
Expand Down
5 changes: 1 addition & 4 deletions tests/aws_signature_v4/test_aws4auth_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,7 @@ def test_aws_auth_share_security_tokens_between_instances(
service="iam",
security_token="security_token",
)
assert auth2.include_headers == {
"content-type",
"host",
}
assert auth2.include_headers == set()

httpx_mock.add_response(
url="https://authorized_only",
Expand Down

0 comments on commit ffaddf3

Please sign in to comment.