Skip to content

Modules

Top-level package for httpx-auth-awssigv4.

auth

Utility to help with SigV4 authentication for httpx python library.

Reference: https://docs.aws.amazon.com/general/latest/gr/sigv4-signed-request-examples.html

SigV4Auth

Auth class to provide AWS Sigv4 authentication support to httpx library.

When we send API requests to AWS, we need to sign the requests so that AWS can identify who sent them. To sign the requests we use Signature V4 process to sign API requests. Instantiation of this class creates an auth object that python httpx library can use to sign the API requests before sending them to AWS.

Note

This class only supports adding authorization header as part of the request and doesn't support adding authorization header in query string

Usage:

import httpx
from httpx_auth_awssigv4 import Sigv4Auth

auth = Sigv4Auth(
    access_key: "AWS_ACCESS_KEY_ID",
    secret_key: "AWS_SECRET_ACCESS_KEY",
    service: "execute-api",
    region: "us-east-1"
)

response = httpx.get(
    url="https://<API ID>.execute-api.<Region>.amazonaws.com/prod/detials",
    params={"username": "tstark"},
    auth=auth
)

Parameters:

Name Type Description Default
access_key str

AWS access key

required
secret_key str

AWS secret access key

required
service str

Name of the service request is being made to

required
region str

AWS region to which the API request is being sent to

required
token Optional[str]

AWS Session token in case of temporary crendentials. Defaults to None.

required

__call__(self, request) special

The httpx library calls this method before sending the API request.

Parameters:

Name Type Description Default
request Request

API request information as a httpx Request

required

Returns:

Type Description
Request

Request : Modified request signed with AWS crdentials using Signature v4 process

Source code in httpx_auth_awssigv4/auth.py
def __call__(self, request: Request) -> Request:
    """The httpx library calls this method before sending the API request.

    Args:
        request (Request): API request information as a httpx Request

    Returns:
        Request : Modified request signed with AWS crdentials using Signature v4 process
    """
    # Create a date for headers and the credential string
    current_time = datetime.utcnow()
    timestamp = current_time.strftime("%Y%m%dT%H%M%SZ")
    datestamp = current_time.strftime("%Y%m%d")  # Date w/o time, used in credential scope

    # CREATE A CANONICAL REQUEST
    canonical_request = self.get_canonical_request(request=request, timestamp=timestamp)

    # CREATE THE STRING TO SIGN

    credential_scope = f"{datestamp}/{self._region}/{self._service}/aws4_request"
    string_to_sign = (
        f"{self._algorithm}\n{timestamp}\n{credential_scope}\n"
        f'{hashlib.sha256(canonical_request.encode("utf-8")).hexdigest()}'
    )

    # CALCULATE THE SIGNATURE
    signing_key = self.get_signature_key(datestamp)
    signature = hmac.new(signing_key, (string_to_sign).encode("utf-8"), hashlib.sha256).hexdigest()

    # ADD SIGNING INFORMATION TO THE REQUEST

    authorization_header = self.get_authorization_header(credential_scope=credential_scope, signature=signature)

    headers = {
        "x-amz-date": timestamp,
        "Authorization": authorization_header,
    }

    if self._token:
        headers["X-Amz-Security-Token"] = self._token

    request.headers.update(headers)

    return request

__init__(self, access_key, secret_key, service, region, token=None) special

Auth class to provide AWS Sigv4 authentication support to httpx library.

Parameters:

Name Type Description Default
access_key str

AWS access key

required
secret_key str

AWS secret access key

required
service str

Name of the service request is being made to

required
region str

AWS region to which the API request is being sent to

required
token Optional[str]

AWS Session token in case of temporary crendentials. Defaults to None.

None
Source code in httpx_auth_awssigv4/auth.py
def __init__(self, access_key: str, secret_key: str, service: str, region: str, token: Optional[str] = None):
    """Auth class to provide AWS Sigv4 authentication support to httpx library.

    Args:
        access_key (str): AWS access key
        secret_key (str): AWS secret access key
        service (str): Name of the service request is being made to
        region (str): AWS region to which the API request is being sent to
        token (Optional[str], optional): AWS Session token in case of temporary crendentials. Defaults to None.
    """

    self._access_key = access_key
    self._secret_key = secret_key
    self._token = token
    self._service = service
    self._region = region

    self._signed_headers = "host;x-amz-date"

    # the hashing algorithm that you use to calculate the digests in the canonical request
    self._algorithm = "AWS4-HMAC-SHA256"

get_authorization_header(self, credential_scope, signature)

Constructs "Authorization" header to include in the request.

Parameters:

Name Type Description Default
credential_scope str

String that includes the date, the Region you are targeting, the service you are requesting, and a termination string

required
signature str

calculated signature to include in Authorization header

required

Returns:

Type Description
str
Source code in httpx_auth_awssigv4/auth.py
def get_authorization_header(self, credential_scope: str, signature: str) -> str:
    """Constructs "Authorization" header to include in the request.

    Args:
        credential_scope (str): String that includes the date, the Region you are targeting, the service you are
            requesting, and a termination string
        signature (str): calculated signature to include in Authorization header

    Returns:
        str: String to send under "Authorization" header
    """
    return (
        f"{self._algorithm} Credential={self._access_key}/{credential_scope},"
        f" SignedHeaders={self._signed_headers}, Signature={signature}"
    )

get_canonical_request(self, request, timestamp)

Creates a canonical request.

This function returns information from your request in a standardized (canonical) format. Read more about Cananolical requests below https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html

Parameters:

Name Type Description Default
request Request

httpx Request object

required
timestamp str

The date is specified with ISO8601 basic format in the x-amz-date header in the format YYYYMMDD'T'HHMMSS'Z'

required

Returns:

Type Description
str
Source code in httpx_auth_awssigv4/auth.py
def get_canonical_request(self, request: Request, timestamp: str) -> str:
    """Creates a canonical request.

    This function returns information from your request in a standardized (canonical) format. Read more about
    Cananolical requests below
    https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html

    Args:
        request (Request): httpx Request object
        timestamp (str): The date is specified with ISO8601 basic format in the x-amz-date header in the format
            YYYYMMDD'T'HHMMSS'Z'

    Returns:
        str: request infromation in a canonical format
    """
    canonical_uri = request.url.path
    canonical_querystring = request.url.query.decode("utf-8")
    canonical_headers = f"host:{request.url.host}\nx-amz-date:{timestamp}\n"

    if request.content:
        payload_hash = hashlib.sha256(request.content).hexdigest()
    else:
        payload_hash = hashlib.sha256(("").encode("utf-8")).hexdigest()

    canonical_request = (
        f"{request.method}\n{canonical_uri}\n{canonical_querystring}\n"
        f"{canonical_headers}\n{self._signed_headers}\n{payload_hash}"
    )
    return canonical_request

get_signature_key(self, date_stamp)

Creates a signing key derived from secret key.

Read more about creating a signing key in the following page Ref: https://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html

Parameters:

Name Type Description Default
date_stamp str

date used in credential scope, should be in '%Y%m%d' format

required

Returns:

Type Description
bytes
Source code in httpx_auth_awssigv4/auth.py
def get_signature_key(self, date_stamp: str) -> bytes:
    """Creates a signing key derived from secret key.

    Read more about creating a signing key in the following page
    Ref: https://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html

    Args:
        date_stamp (str): date used in credential scope, should be in '%Y%m%d' format

    Returns:
        str: Signing key derived from secret key

    """

    def sign(key, msg):
        return hmac.new(key, msg.encode("utf-8"), hashlib.sha256).digest()

    date = sign(("AWS4" + self._secret_key).encode("utf-8"), date_stamp)
    region = sign(date, self._region)
    service = sign(region, self._service)
    signing = sign(service, "aws4_request")
    return signing