Signing AWS Requests with Curl

All requests to AWS APIs must be signed using AWS Signature Version 4 (SigV4). While the AWS SDK libraries and the AWS CLI will handle this for you, external HTTP client libraries are on their own to support the SigV4 signing process.

This matters if you have built an application using API Gateway and IAM authorization. Client requests, whether it be from a mobile application or testing script, need to be signed. Fortunately, the open-source community has contributed support for SigV4 in many popular HTTP libraries (e.g., Python’s Requests, Dart’s Dio). Notably, the command line tool, curl, has native support for AWS signing.

The curl incantation is tricky enough that I have to google and fuss about it a bit to get it right. Here are some common ways of signing your requests with curl.

If you are still using long-term access keys this will do:

curl \
  --request POST \
  --user "$AWS_ACCESS_KEY:$AWS_SECRET_ACCESS_KEY" \
  --aws-sigv4 "aws:amz:us-east-1:execute-api" \
  --header "Content-Type: application/json" \
  "$URL"

Of course, that approach is no longer recommended, and you should use short-lived credentials. With short-lived credentials you’ll have to include session information with your temporary access and secret key.

curl \
  --request POST \
  --user "$AWS_ACCESS_KEY:$AWS_SECRET_ACCESS_KEY" \
  --aws-sigv4 "aws:amz:us-east-1:execute-api" \
  --header "x-amz-security-token: $AWS_SESSION_TOKEN" \
  --header "Content-Type: application/json" \
  "$URL"

Note, AWS_ACCESS_KEY, AWS_SECRET_ACCESS_KEY, and AWS_SESSION_TOKEN are probably not in your environment. You will need to set them.

If you have programatic access (via the CLI) to the AWS account the API is in, and your permissions are sufficient, you can use the values in cached by the AWS CLI cache after a login.

ls -lt ~/.aws/cli/cache

You will likely want the most recent file there.

Better still is to use the CLI itself to get at those values.

aws configure export-credentials --format process

You can parse the json output of that command and automate the steps needed to run your curl. Note, the aws CLI won’t respect the --query option for the export-credentials command - I presume to avoid piping sensitive data to external processes - so you will have to parse the output with a tool you trust. I use jq in the command below.

curl 
 --request POST \
 --aws-sigv4 "aws:amz:us-east-1:execute-api" \
 --user "$(aws configure export-credentials --format process | jq -r .AccessKeyId):$(aws configure export-credentials --format process | jq -r .SecretAccessKey)" \
 --header "x-amz-security-token: $(aws configure export-credentials --format process | jq -r .SessionToken)" \
 --header "Content-Type: application/json" \
 "$URL"

And there you have it. Not the fastest but I think it looks pretty good.