Headers and presigned URLs · SigV4a · Chunked uploads and event streams · No dependencies
Fast AWS Signature Version 4 request signing for every AWS service, with no dependencies. It runs on Node.js, Bun, Deno, Cloudflare Workers and browsers.
import { getAuthHeaders } from 'sigflint';
const url = new URL('https://my-bucket.s3.us-east-1.amazonaws.com/photos/cat.jpg');
const headers = await getAuthHeaders(
{ method: 'GET', url, region: 'us-east-1' },
{ accessKeyId: process.env.AWS_ACCESS_KEY_ID, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY },
);
const response = await fetch(url, { headers });It signs requests with the Authorization header or as a presigned URL, for S3 and for any other service (service: 'dynamodb', 'sqs', 'execute-api', ...), and it also does multi-region signatures (SigV4a), chunked uploads and event streams.
- Correct. It returns the same signature as botocore for all 3,360 S3 requests in its test corpus, and for 1,622 requests to other services and 1,600 presigned URLs. For S3, botocore signs the request after this repository's own path and query normalization (
conformance/oracle.py), so that step is not checked independently by the corpus; the four S3 examples published by AWS, which are in it, are. SigV4a signatures verify against the AWS CRT's, and every chunk and event signature in the test corpus matches the AWS SDK's. See how it is tested. - Fast. 1.4 to 3.6 µs per header or presigned-URL signature on Node and 1.5 to 4.8 µs on Bun. The other libraries take 2.6 to 517 times as long on the requests they sign correctly (five Bun presigned-URL comparisons, with
aws4,@smithy/signature-v4andminio, are not clear and are left out). See speed. - Small, and you pay only for what you import. Signing headers is 5.7 kB minified, 2.8 kB gzipped; presigned URLs, SigV4a, chunked uploads and event streams each add to a bundle only when imported. Nothing to install with it. See size.
- Everywhere. One import on Node, Bun, Deno and Cloudflare Workers, with or without
nodejs_compat. See runtimes.
npm install sigflintNode.js 22.12 or newer (it is an ES module; Node 22.12 can also require() it). Bun and Deno need no setup.
Sign a request and send the returned headers with it. The host header is signed but not returned; your HTTP client sets it from the URL.
import { getAuthHeaders } from 'sigflint';
const credentials = { accessKeyId, secretAccessKey, sessionToken }; // sessionToken is optional
// upload an object: the body is hashed into x-amz-content-sha256
const url = new URL('https://my-bucket.s3.us-east-1.amazonaws.com/notes/today.json');
const body = JSON.stringify({ done: true });
const headers = await getAuthHeaders(
{ method: 'PUT', url, region: 'us-east-1', body, signHeaders: { 'content-type': 'application/json' } },
credentials,
);
await fetch(url, { method: 'PUT', headers, body });For a large or binary body, skip hashing and send unsignedPayload: true. S3 accepts that over HTTPS.
const headers = await getAuthHeaders({ method: 'PUT', url, region, unsignedPayload: true }, credentials);For another S3-compatible service, pass its region name and URL (https://us-sea-1.linodeobjects.com/bucket/key, http://127.0.0.1:9000/bucket/key). These are examples of the form, not endpoints that were tried. Signatures are checked against botocore, the AWS CRT, the AWS SDK's signer and the AWS documentation examples. Nothing has been sent to AWS S3, so a service that canonicalizes requests differently from S3 may reject them. The only server tried is a local s3s-fs 0.17.0 test server on loopback, once, with header-signed requests (create a bucket, PUT, GET, list): it accepted them, except that it rejected sigflint's signature for a raw + in the path, so write %2B (how the path is read).
More in the guide: other services, presigned URLs, SigV4a, chunked uploads, event streams, why to always await, and the full API.
Node.js 22.12 and later, Bun, Deno, and Cloudflare Workers with or without nodejs_compat; browsers get the same WebCrypto code. Each runtime was checked against botocore's signature for one S3 request. The grid, with the other libraries, is in the benchmarks.
Correctness, speed, memory and size, the test corpora and the methodology and limits are in docs/benchmarks.md. Every figure there comes from raw results committed in bench/results/. The speed, memory and import figures were measured on the code in this release and are re-run before each release.
1.0 signs requests for every AWS service in header form and as presigned URLs, plus SigV4a, chunked uploads and event streams. Not included, and not promised:
- hashing binary request bodies for you: hash them yourself, or use
unsignedPayload - an HTTP client, retries, credential providers or region lookup
- anything that is not SigV4 or SigV4a
Open an issue describing the use case if you need one of them.
See SECURITY.md. sigflint never logs credentials or writes them anywhere. Its signing-key caches keep secret keys in process memory: each cached signing key (up to 1,000 per cache) is stored under a lookup string that contains the secret access key it was derived from.
sigflint is built and maintained by one person. If it saves you time, you can sponsor it monthly or once through GitHub Sponsors, or leave a tip on Ko-fi.
MIT. CODE_OF_CONDUCT.md is the Contributor Covenant under CC BY 4.0; see THIRD_PARTY_NOTICES.md.