Skip to content

feat(signer): add SigV4 request signing utility#5344

Draft
dreamorosi wants to merge 4 commits into
mainfrom
feat/signer
Draft

feat(signer): add SigV4 request signing utility#5344
dreamorosi wants to merge 4 commits into
mainfrom
feat/signer

Conversation

@dreamorosi

Copy link
Copy Markdown
Contributor

Summary

Changes

This PR introduces a new @aws-lambda-powertools/signer package that signs HTTP requests to AWS services using the AWS Signature Version 4 (SigV4) signing process, so customers can call IAM-authenticated endpoints (API Gateway, Lambda function URLs, AppSync) from within their Lambda functions.

The design intentionally splits signing from sending:

  • SigV4Signer (/sigv4) — signs web-standard Request objects and returns a signed Request. Performs no network I/O. Reads credentials and region from the Lambda runtime environment variables by default (no extra dependencies, no cold-start cost from the credential provider chain).
  • createSignedFetcher (/fetch) — a drop-in signed fetch that consumes a signer instance and signs each request before sending it. Composes with libraries that accept a custom fetch.
  • Signing without fetching is first-class: the signed Request exposes url, method, and headers for use with any HTTP client (axios, got, interceptors, generated SDK clients).

Design decisions worth highlighting for review:

  • A structural Signer interface is defined, and the fetcher depends on it (not the concrete class), so a future SigV4aSigner can be added at a /sigv4a subpath as a non-breaking change with its heavier dependencies isolated to that entry point.
  • Subpath exports from day one (/sigv4, /fetch, /errors, /types) — no root export — to keep import-level isolation and make future signer variants additive.
  • Opinionated, lean credentials: defaults to the standard AWS env vars the Lambda runtime always injects (including AWS_SESSION_TOKEN), avoiding a dependency on @aws-sdk/credential-provider-node. Customers can pass explicit credentials (e.g. fromNodeProviderChain()) for non-Lambda environments.
  • Only runtime dependencies are @smithy/signature-v4 and @smithy/types; hashing uses node:crypto (no @aws-crypto/sha256-js). We do not depend on @smithy/protocol-http — the signing process accepts a plain object typed via @smithy/types, and a guard test protects that contract.
  • Typed errors (/errors): base SignerError, plus SignerConfigError (missing region at construction / unresolvable credentials) and RequestSigningError (signing failures, including non-bufferable streaming bodies). Transport errors from the underlying fetch propagate untouched.

Out of scope / deferred (accommodated by the design): SigV4a, streaming/UNSIGNED-PAYLOAD bodies (would arrive as additive options or a future signStream method), service auto-detection from the URL, and uriEscapePath/payload-signing knobs.

Includes: unit tests with 100% coverage, a feature page (docs/features/signer.md), package README, doc snippets, navigation/index updates, and the signer semantic-commit scope.

Please add the issue number below, if no issue is present the PR might get blocked and not be reviewed

Issue number: closes #2146


By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

Disclaimer: We value your time and bandwidth. As such, any pull requests created on non-triaged issues might not be successful.

Add a new `@aws-lambda-powertools/signer` package that signs HTTP requests
using the AWS Signature Version 4 (SigV4) signing process.

- `SigV4Signer` (/sigv4): signs web-standard `Request` objects, reading
  credentials and region from the Lambda runtime by default
- `createSignedFetcher` (/fetch): drop-in signed `fetch` that consumes a signer
- structural `Signer` interface so future variants (e.g. SigV4a) compose
  without breaking changes
- typed errors (/errors): `SignerError`, `SignerConfigError`,
  `RequestSigningError`
- only depends on `@smithy/signature-v4` and `@smithy/types`; uses
  `node:crypto` for hashing
- unit tests with 100% coverage, docs feature page, README, and snippets

Closes #2146
@powertools-for-aws-oss-automation powertools-for-aws-oss-automation Bot added the size/XXL PRs with 1K+ LOC, largely documentation related label Jun 12, 2026
Comment thread packages/signer/tests/unit/Sha256.test.ts Fixed
- Sha256: use positive condition in ternary (S7735)
- fetch: remove unnecessary type assertion (S4325)
@powertools-for-aws-oss-automation powertools-for-aws-oss-automation Bot added size/XXL PRs with 1K+ LOC, largely documentation related and removed size/XXL PRs with 1K+ LOC, largely documentation related labels Jun 12, 2026
- add e2e suite: caller Lambda signs requests against an IAM-protected
  REST API (mock integration), asserting unsigned->403 and signed->200
  across the signing entry points, body/query signing, signer reuse,
  drop-in fetch, region and credentials options, and the
  RequestSigningError path for an unreadable body
- wire test:e2e* scripts, testing-utils devDep, and vitest e2e timeouts
- add packages/signer to the e2e CI matrix
- correct the RequestSigningError message and docs: finite streams are
  buffered and signed fine; only unreadable/non-replayable bodies fail
@powertools-for-aws-oss-automation powertools-for-aws-oss-automation Bot added size/XXL PRs with 1K+ LOC, largely documentation related and removed size/XXL PRs with 1K+ LOC, largely documentation related labels Jun 12, 2026
@powertools-for-aws-oss-automation powertools-for-aws-oss-automation Bot added size/XXL PRs with 1K+ LOC, largely documentation related and removed size/XXL PRs with 1K+ LOC, largely documentation related labels Jun 12, 2026
@sonarqubecloud

sonarqubecloud Bot commented Jun 12, 2026

Copy link
Copy Markdown

Quality Gate Passed Quality Gate passed

Issues
0 New issues
0 Accepted issues

Measures
0 Security Hotspots
No data about Coverage
0.0% Duplication on New Code

See analysis details on SonarQube Cloud

this.#hash =
secret === undefined
? createHash('sha256')
: createHmac('sha256', secret as Uint8Array | string);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How come we need these casts?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question - let me investigate

const ENVIRONMENT_VARIABLES = process.env;

beforeEach(() => {
process.env = {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use the vitest stubEnv/unstubEnv fucntions

Comment thread packages/signer/README.md

Powertools for AWS Lambda (TypeScript) is a developer toolkit to implement Serverless [best practices and increase developer velocity](https://docs.aws.amazon.com/powertools/typescript/latest/#features).

You can use the package in both TypeScript and JavaScript code bases.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this necessary? JavaScript is valid TypeScript.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes fair, this is a copy of other readme files that used to do this type of statement but agreed I'll remove it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/XXL PRs with 1K+ LOC, largely documentation related

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature request: module to Sign Requests

3 participants