Giter VIP home page Giter VIP logo

requests-auth-aws-sigv4's Introduction

requests-auth-aws-sigv4

Use AWS signature version 4 Authentication with the python requests module

This package provides an authentication class that can be used with the popular requests package to add the AWS Signature Version 4 authentication information.

The signing code is inspired by the python example provided by AWS.

This package should support any/all AWS API's, including API Gateway API's (execute-api), Elasticsearch clusters, and others. AWS Credentials may be pulled from the environment in an easy and familiar way. The signature is added as a header to the request.

Installation

pip install requests-auth-aws-sigv4

Usage

import requests
from requests_auth_aws_sigv4 import AWSSigV4

r = requests.request('POST', 'https://sts.us-east-1.amazonaws.com', 
    data=dict(Version='2011-06-15', Action='GetCallerIdentity'), 
    auth=AWSSigV4('sts'))
print(r.text)

If boto3 is available, it will attempt to use credentials that have been configured for the AWS CLI or SDK's, as documented in Boto3 User Guide: Credentials. Otherwise, if boto3 is not available, credentials must be provided using either environment variables or parameters.

Example using environment variables

Environment variable names are the same as documented for AWS CLI and SDK's.

export AWS_ACCESS_KEY_ID=MYACCESSKEY
export AWS_SECRET_ACCESS_KEY=THISISSECRET
export AWS_SESSION_TOKEN=THISISWHERETHESUPERLONGTOKENGOES
import requests
from requests_auth_aws_sigv4 import AWSSigV4

aws_auth = AWSSigV4('ec2') # If not provided, check for AWS Credentials from Environment Variables

r = requests.request('GET', 'https://ec2.us-east-1.amazonaws.com?Version=2016-11-15&Action=DescribeRegions',
    auth=aws_auth)
print(r.text)

Example using parameters

Passing credentials as parameters overrides all other possible sources.

import requests
from requests_auth_aws_sigv4 import AWSSigV4

aws_auth = AWSSigV4('ec2',
    aws_access_key_id=ACCESS_KEY,
    aws_secret_access_key=SECRET_KEY,
    aws_session_token=SESSION_TOKEN,
)

r = requests.request('GET', 'https://ec2.us-east-1.amazonaws.com?Version=2016-11-15&Action=DescribeRegions',
    auth=aws_auth)
print(r.text)

Usage with Elasticsearch Client (elasticsearch-py)

from elasticsearch import Elasticsearch, RequestsHttpConnection
from requests_auth_aws_sigv4 import AWSSigV4

es_host = 'search-service-foobar.us-east-1.es.amazonaws.com'
aws_auth = AWSSigV4('es')

# use the requests connection_class and pass in our custom auth class
es_client = Elasticsearch(host=es_host,
                          port=80,
                          connection_class=RequestsHttpConnection,
                          http_auth=aws_auth)
es_client.info()

Debug Logging

All log messages are at the module level.

import logging
logging.basicConfig() # Setup basic logging to stdout
log = logging.getLogger('requests_auth_aws_sigv4')
log.setLevel(logging.DEBUG)

Command Line Usage

The module can be run from the command line in a way that is similar to how cURL works.

$ python3 -m requests_auth_aws_sigv4 https://sampleapi.execute-api.us-east-1.amazonaws.com/test/ -v
> GET /test/ HTTP/1.1
> Host: sampleapi.execute-api.us-east-1.amazonaws.com
> User-Agent: python-requests/2.23.0 auth-aws-sigv4/0.2
> Accept-Encoding: gzip, deflate
> Accept: */*
> Connection: keep-alive
> X-AMZ-Date: 20200513T180549Z
> Authorization: AWS4-HMAC-SHA256 Credential=AKIASAMPLEKEYID/20200513/us-east-1/execute-api/aws4_request, SignedHeaders=host;x-amz-date, Signature=EXAMPLESIGNATUREISHERE
>
< HTTP/1.1 200 OK
< Connection: keep-alive
< Content-Length: 25
< Content-Type: application/json
< Date: Wed, 13 May 2020 18:05:49 GMT
< Server: Server
< x-amz-apigw-id: MeExampleiMFs99=
< x-amzn-RequestId: 7example-7b7b-4343-9a9a-9bbexampleaf
hello

Temporary Security Credentials

Credentials issued from AWS STS to grant temporary access can be used normally. Set the token by passing the aws_session_token parameter, setting the AWS_SESSION_TOKEN environment variable, or configure the credential for boto3 as normal.

Using boto3 (or botocore) for AWS Credentials

The packages boto3 and botocore are not requirements to use this module.
As mentioned above, if boto3 is available, a boto3.Session will be created to attempt to get credentials and configure the default region. This will happen automatically if credentials are not provided as parameters.

requests-auth-aws-sigv4's People

Contributors

andrewjroth avatar evladimirou01 avatar jmennius avatar lm-james avatar rajarahman17 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

requests-auth-aws-sigv4's Issues

Create payloadFromHashAssumes that the request body is either string or byte - throws exceptions for type `_io.BufferedReader`

Came across this library when using mlflow.log_artifacts with AWS_SIGV4 request signing enabled. When trying to upload the artifact with a put call, this library throws an exception saying that _io.BufferedReader cannot be encoded. Possible solution (or a better version of it).
At line https://github.com/andrewjroth/requests-auth-aws-sigv4/blob/master/requests_auth_aws_sigv4/__init__.py#L141C14-L141C14

  if r.body:
                if isinstance(r.body, bytes):
                    log.debug("Request Body: <bytes> %s", r.body)
                    payload_hash = hashlib.sha256(r.body).hexdigest()
                elif isinstance(r.body, bytes):
                    log.debug("Request Body: <str> %s", r.body)
                    payload_hash = hashlib.sha256(r.body.encode('utf-8')).hexdigest()
                else
                     log.debug("Request Body Object's instance type: {type(obj)} ")
                     body_buffer=r.body
                     file_content=body_buffer.read()
                     body_buffer.seek(0)
                     payload_hash = payload_hash = hashlib.sha256(file_content).hexdigest()

Missing required header for requests with 'aws_session_token'

How to reproduce ?

import requests
from requests_auth_aws_sigv4 import AWSSigV4

ACCESS_KEY = 'XXXXXXXX'
SECRET_KEY = 'XXXXXX'
SESSION_TOKEN = 'XXXXX'

aws_auth = AWSSigV4('s3',
    aws_access_key_id=ACCESS_KEY,
    aws_secret_access_key=SECRET_KEY,
    aws_session_token=SESSION_TOKEN,
    region= 'us-east-1',
)

r = requests.request('GET', 'https://<bucket_name>.s3.amazonaws.com/<location_on_s3>',
    auth=aws_auth)
print(r.text)

This gives following error message -

Missing required header for this request: x-amz-content-sha256

Pls.note this access_key, secret_key and session_token are valid and I have checked that using other packages.

Access token is missing in the request header

I am seeing the following issue and am not sure where I would add the access_token from reading your examples.

{
  "errors": [
    {
      "message": "Access to requested resource is denied.",
     "code": "Unauthorized",
     "details": "Access token is missing in the request header."
    }
  ]
}

AWS signature canonical string and query not urlencoded.

Hey,

I am forming the Authorisation header and I am not using the Boto libraries. While forming the canonical uri and canonical querystring the aws documentation(https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html ) says that we need to urlencode them. However, i do not see any urlencoding in the init.py file. Can you pls. check if that's missing or there is some gap in my understanding ? This is with reference to the s3

Can't use on a long-lived requests.Session

If you set the auth of a requests.Session to an AWSSigV4 object, after the token expires (e.g. 15 minutes), requests no longer succeed. The __call__ method should check whether the credentials are expired and refresh them before updating the request.

Doesn't work when request.body is a bytes (and not a string)

The request.body is a bytes (as opposed to a string).
The following snippet reproduces the error

package versions:
requests==2.25.1
requests-auth-aws-sigv4==0.4

import requests
from requests_auth_aws_sigv4 import AWSSigV4

url = 'https://google.com'
requests.post(url='https://google.com', json=['test'], auth=AWSSigV4('execute-api'))

and the error:

  ...
  File ".../lib/python3.6/site-packages/requests/models.py", line 551, in prepare_auth
    r = auth(self)
  File ".../python3.6/site-packages/requests_auth_aws_sigv4/__init__.py", line 138, in __call__
    payload_hash = hashlib.sha256(r.body.encode('utf-8')).hexdigest()
AttributeError: 'bytes' object has no attribute 'encode'

suggested fix:
when computing the hexdigest, not call encode(''utf-8') if request.body is already of type bytes

Bad signature for request with repeated key in query parameters

I've got a request that's getting a 403
The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.

The request specifies the "created" query parameter key twice like so:
created=ge2000-07-01&created=le2024-03-09

If we use this syntax, the request is accepted.
created=ge2000-07-01,le2024-03-09

Since the original request works fine with AWS signatures in Postman, I'm thinking the duplicated query param key is causing the signature to be generated incorrectly. Please let me know if I can furnish any additional details.

Invalid token with temporary session token

This is working great for me when using basic access key id and secret access key values. However, if I'm using a temporary token, I get an error when calling AWS api due to an invalid token. I was able to confirm requests-auth-aws-sigv4 is picking up the session token from my credentials file, but best guess is it isn't creating the signed header correctly?

AWSSigV4 should not overwrite existing headers such as Content-Type or User-Agent

The following request is modified by the AWSSigV4 which adds the signature but also changes the Content-Type header.

requests.get(url, auth=AWSSigV4(service='execute-api'), headers={'Content-Type': 'application/json'})

Changing the Content-Type to "application/x-www-form-urlencoded; charset=utf-8" may cause the request to fail on the server side.

The sigv4 spec doesn't require signing the Content-Type header, so by simply removing this update to the headers dictionary I was able to work-around the issue. Alternatively, the header could be signed if it exists.

I don't think overwriting the User-Agent is a good idea either.

I'd be happy to submit a PR if you could provide some guidance on how you'd like to handle the existing headers

AWSSigV4 not thread safe

We were using a cached AWSSigV4 object which was shared between threads, and we'd very occasionally get a 403 error because the signature was wrong.
Looking at the code, it looks like it uses data members to store some timestamp related values when it's calculating the signing values:

self.amzdate = t.strftime('%Y%m%dT%H%M%SZ')

I know the workaround for us is easy: don't treat the AWSSigV4 as if it is thread safe. But as far as I can tell, it would be pretty easy to make it thread safe by using local variables instead of member variables. Have I missed something as to why member variables are used in this way?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.