For upgrades from version 1.x to 2.0.0, follow the migration guide.
This plugin is borrows heavily from the original work by Nick Statterly (https://github.com/guardian/httpie-hmac-auth) - that project was archived in May 2022.
This plugin extends the functionality to allow different HMAC patterns to be defined in the library and by a user provided script - thereby avoiding any requirement to create a new plugin to support a different pattern.
The httpie auth should be set to hmac and the --auth field contains key-value pairs to configure the plugin, the keys are:
secret- base64 encoded secret to be used in the HMACaccess_id- (Optional) String access token / id used to identify the user depending on the schemaformat- (Optional) Sets a pre-defined format or a python file to process the headerssession_token- (Optional, AWS4 only) Session token for temporary AWS credentialsmax_body_size- (Optional) Maximum upload size in bytes; defaults to 104857600 (100 MiB)
Key-value pairs can also be set using environment variables starting with HTTPIE_HMAC_.
The first colon separates each key from its value; additional colons are kept
in the value, so host:localhost:9000 is supported. Commas separate settings.
For example:
http --auth-type=hmac --auth="secret:some_secret" GET http://localhost:8000
http --auth-type=hmac --auth="secret:7Ez...wVA,access_id:AK...6R,format:aws4" GET https://my_bucket.s3.eu-west-2.amazonaws.com/file.txt
export HTTPIE_HMAC_SECRET=7Ez...wVA
export HTTPIE_HMAC_ACCESS_ID=AK...6R
export HTTPIE_HMAC_FORMAT=aws4
httpie --auth-type=hmac --auth="" GET https://my_bucket.s3.eu-west-2.amazonaws.com/file.txtAWS4 uses the AWSRequestsAuth library to generate the required AWS auth header. It will attempt to get the required information from the provided URL, however the host, region and service fields can be set manually.
Explicit region and service settings each override the inferred value.
Inference supports service.region.amazonaws.com, regional S3 bucket endpoints
(including dualstack), API Gateway API endpoints, and OpenSearch domain
endpoints. Other layouts require explicit region and service settings.
Hostname service names that differ from the AWS signing service name also need
an explicit service setting. The host setting overrides the signing host;
inference still uses the request URL.
For example:
http --auth-type=hmac --auth="secret:7Ez...wVA,access_id:AK...6R,host:my_bucket.s3.eu-west-2.amazonaws.com,service:s3,region:eu-west-2,format:aws4" GET https://my_bucket.s3.eu-west-2.amazonaws.com/file.txt
For temporary AWS credentials (such as credentials obtained through STS or an
assumed role), supply the session token alongside the matching access key ID and
secret access key. The AWS4 signer includes it as X-Amz-Security-Token:
export HTTPIE_HMAC_ACCESS_ID="$AWS_ACCESS_KEY_ID"
export HTTPIE_HMAC_SECRET="$AWS_SECRET_ACCESS_KEY"
export HTTPIE_HMAC_SESSION_TOKEN="$AWS_SESSION_TOKEN"
http --auth-type=hmac --auth="format:aws4" GET https://my-bucket.s3.eu-west-2.amazonaws.com/file.txtYou can also pass session_token:YOUR_SESSION_TOKEN in the comma-separated
--auth settings. Command-line settings override the corresponding
HTTPIE_HMAC_ environment variables. Supply the AWS secret access key as issued,
without decoding it. Credentials must be supplied explicitly; this plugin does
not discover AWS profiles or refresh expired credentials. When switching to
long-lived credentials, unset HTTPIE_HMAC_SESSION_TOKEN.
The string_to_sign consists of the HTTP method, content_md5, content_type, http_date and path:
[method]\n
[content_md5]\n
[content_type]\n
[http_date]\n
[path]
This string is signed using the sha256 HMAC. The resulting signature is placed in the "Authorization" header in the format:
Authorization: HMAC [signature]
Authorization: HMAC [access_id]:[signature]
A custom python file can be passed to the plug and used to generate bespoke formats, the following example implements the Simple formatter using a custom file:
import hmac
import hashlib
import base64
from httpie_hmac import HmacGenerate
class HmacAuthCustom(HmacGenerate):
def generate(request):
string_to_sign = '\n'.join(
[request.method, request.content_md5, request.content_type,
request.http_date, request.path]).encode()
digest = hmac.new(bytes(request.secret_key, 'UTF-8'), string_to_sign,
hashlib.sha256).digest()
signature = base64.b64encode(digest).rstrip().decode('utf-8')
if request.access_id is None or request.access_id == '':
request.inner.headers['Authorization'] = f"HMAC {signature}"
else:
request.inner.headers['Authorization'] = \
f"HMAC {request.access_id}:{signature}"
return request.inner
Note that the request.inner.headers dictionary will contain content_type, content_md5 and date fields if they were not previously set. If they are not required they need to be removed from the list.
Additional data could be passed to the custom formatter using environment variables if needed.
Uploads are hashed before transmission, including multipart and chunked bodies. Seekable binary inputs are read in 64 KiB chunks and rewound to their original position. Nonseekable and text inputs use a temporary spool, which holds up to 1 MiB of payload in memory before rolling over to disk. Chunked framing is preserved.
Every body has a default maximum of 100 MiB (104857600 bytes). Set
max_body_size:209715200 in --auth, or export
HTTPIE_HMAC_MAX_BODY_SIZE=209715200, to allow up to 200 MiB. CLI settings take
precedence. The limit must be a positive integer; unlimited values are not
accepted. It counts actual payload bytes, including UTF-8 encoding, independently
of a supplied Content-Length. Preparation stops with an error when the limit
is exceeded, before transmission. This counts payload bytes, excluding wire
chunk-framing overhead, and bounds spool disk usage per request. Concurrent
uploads have separate limits. It does not bound allocations made by HTTPie or a
custom input producer before this plugin receives the body, or time spent
waiting for a blocked input source.
Custom formatters should support a seekable file-like request.inner.body as
well as bytes. Reading it must preserve/restore its position before returning.
Small nonseekable inputs are converted to bytes; larger spools remain file-like.
Caller-owned streams are never closed by the plugin.
Temporary files close on preparation/signing errors and on the final HTTP
response; redirects retain the body for replay. Python callers should also close
a file-like prepared body in a finally block after sending or previewing it,
especially on network errors. HTTPie's auth API has no offline-completion or
network-error cleanup hook, so offline/abandoned requests additionally rely on
object cleanup to release their spool. A closed body cannot be replayed.
HTTPie's request-body output callbacks can run during preparation; spooled
uploads may not appear in verbose request-body output.
Do not combine HMAC authentication with HTTPie's --compress option (including
repeated --compress). HTTPie applies request compression after this plugin
calculates its checksums and signature. If the body is compressed, the generated
Content-MD5 no longer matches the payload; AWS4's x-amz-content-sha256 and
signature also describe the original body. This affects Simple, AWS4, and custom
formats that sign the body or its checksums, including offline request previews.
The plugin does not detect or reject this combination. Omit --compress when
using HMAC authentication. Supporting it requires an HTTPie authentication hook
after request transformations and before preview or transmission.
Install the package in a virtual environment before running the suite so HTTPie can discover the authentication plugin through its installed entry point:
python -m venv .venv
source .venv/bin/activate
python -m pip install .
python -m unittest discover -s tests -vThe tests use dummy credentials and run offline, including the HTTPie CLI tests. They require no AWS account or secrets. CI runs the same suite on Python 3.11 and 3.13; the existing live AWS smoke test is a separate job. Compression regression tests are marked as expected failures until HTTPie provides the integration needed to sign the final payload.