AWS Signature V4 Express Middleware

raw JSON →
0.14.1 verified Thu Apr 23 auth: no javascript

aws4-express is an Express middleware library designed for validating AWS Signature Version 4 (SigV4) authenticated requests. It enables Express applications to act as SigV4-secured endpoints, mimicking AWS service behavior without requiring an actual AWS backend for authentication. The current stable version is 0.14.1, and the project demonstrates an active release cadence, frequently updating to support newer Node.js versions (currently >=20) and incorporating security enhancements. Its primary function is to abstract the complexities of SigV4 verification, providing an `awsVerify` middleware that integrates with Express's request lifecycle, notably requiring careful handling of the raw request body for accurate signature calculation. It is actively maintained with regular updates and security audits.

error Error: SignatureDoesNotMatch
cause The calculated AWS Signature V4 on the server does not match the signature provided by the client. This is often due to an incorrect secret key, a modified request body after client signing, or discrepancies in how the canonical request (headers, query params, method, body hash) is constructed between client and server.
fix
Verify the secretKey callback is returning the correct secret for the accessKey. Ensure no middleware modifies the request body before awsVerify runs, and that rawBodyFromVerify is correctly used. Debug the client-side signing process to match server expectations.
error TypeError: Cannot read properties of undefined (reading 'rawBody')
cause The `awsVerify` middleware attempts to access `req.rawBody` but it is `undefined`. This commonly occurs when an Express body parser is used without the `rawBodyFromVerify` helper attached to its `verify` option, or if no body parser is configured to provide the raw body at all.
fix
If using express.json(), express.raw(), or express.urlencoded(), ensure you pass { verify: rawBodyFromVerify } to its configuration. For custom body parsing, ensure req.rawBody is manually populated with the exact raw request payload.
error ERR_REQUIRE_ESM: require() of ES Module ... Not supported
cause Attempting to `require()` `aws4-express` in a CommonJS module while it is published primarily as an ES Module. While `v0.14.1` fixed the `main` entrypoint, explicit CommonJS `require` might still face issues if the environment expects strict ESM.
fix
Switch to ES Module import syntax (import { awsVerify } from 'aws4-express';) and ensure your project is configured for ESM (e.g., "type": "module" in package.json). If strictly using CommonJS, ensure you are on v0.14.1 or later, which should have improved CJS compatibility due to the entrypoint fix.
breaking Support for Node.js versions below 20.x was officially dropped starting from `v0.13.0`. Users must ensure their environment runs Node.js 20 or higher to use recent versions of this package.
fix Upgrade your Node.js runtime to version 20 or later. For example, using `nvm install 20 && nvm use 20`.
gotcha AWS Signature V4 verification critically depends on the exact, raw request body. If using Express body parsers (like `express.json()`, `express.raw()`, or `express.urlencoded()`), you **must** attach the `rawBodyFromVerify` helper to the `verify` option to ensure `req.rawBody` is populated. Failure to do so will lead to `SignatureDoesNotMatch` errors.
fix Configure your Express body parser middleware with `verify: rawBodyFromVerify`. Example: `app.use(express.json({ verify: rawBodyFromVerify }));`
deprecated The library is currently in a "beta stage" (versions before 1.x.x). This means that minor version updates (e.g., 0.13.x to 0.14.x) may introduce breaking changes without a major version increment. Developers should carefully review the changelog for each update.
fix Consult the `CHANGELOG.md` or GitHub release notes for specific breaking changes when upgrading minor versions. Pin exact versions for production use.
gotcha Version `v0.14.1` fixed an issue where the `main` field in `package.json` was incorrect, potentially causing issues with CJS `require()` statements or incorrect module resolution in some bundlers. While not a direct breaking change, users on older `0.14.0` might have experienced import problems.
fix Upgrade to `v0.14.1` or newer to ensure correct package entrypoint resolution, especially in CommonJS environments.
breaking The initial `v0.4.0` release introduced breaking changes related to configuration parameters. As an alpha release, early users faced interface adjustments.
fix Consult the `v0.4.0` release notes and examples for updated configuration parameter usage. This is mostly relevant for very early adopters.
npm install aws4-express
yarn add aws4-express
pnpm add aws4-express

This quickstart demonstrates how to set up `aws4-express` middleware on an Express server to protect a route with AWS Signature V4, including a client-side example using `aws4` to sign and send a request. It highlights the crucial raw body parsing requirement and dynamic secret key lookup.

import express from 'express';
import { awsVerify, rawBodyFromVerify } from 'aws4-express';
import aws4 from 'aws4';
import https from 'https';

const app = express();

// 1. Important: We need the raw body to check the signature!
//    Must configure Express body parsers to provide rawBody.
app.use(express.json({ verify: rawBodyFromVerify }));

// 2. Add the AWS SigV4 security middleware
app.use(awsVerify({
  secretKey: (message) => {
    // 3. Implement your logic to find the secret for the incoming Access Key
    //    In a real application, this would involve a database lookup.
    if (message.accessKey === 'MY_COOL_KEY') {
      return 'MY_SUPER_SECRET';
    }
    return undefined; // Unknown key? Access denied!
  }
}));

// 4. This route is protected by AWS SigV4 verification
app.get('/secret-club', (req, res) => {
  res.send('Welcome to the VIP area! 🎉 If you see this, your signature was valid.');
});

const PORT = 3000;
app.listen(PORT, () => {
  console.log(`Server listening on http://localhost:${PORT}`);
  console.log('--- Client Test ---');
  // Example client-side usage with aws4 library
  const opts = aws4.sign({
    service: 'execute-api',
    path: '/secret-club',
    method: 'GET',
    host: `localhost:${PORT}`,
    headers: { 'Content-Type': 'application/json' }
  }, {
    accessKeyId: 'MY_COOL_KEY',
    secretAccessKey: 'MY_SUPER_SECRET'
  });

  // Send the signed request
  const clientReq = https.request(opts, res => {
    console.log(`Client received status: ${res.statusCode}`);
    res.pipe(process.stdout);
  });
  clientReq.on('error', (e) => console.error(`Client request error: ${e.message}`));
  clientReq.end();
});