Automatically Sign AWS Requests with Signature V4

// #aws#fetch#javascript#signaturev4#typescript // 2 comments

In a previous article, I explored the challenges of using Lambda Function URLs with IAM authorization and CloudFront custom domains. A key aspect of this setup involves signing HTTP requests with AWS Signature Version 4 (SigV4) to authenticate with IAM.

While the AWS SDK provides utilities for SigV4 signing, the process can be somewhat cumbersome, especially when working with the barebone functions like fetch instead of SDKs. To simplify this process, I've created the aws-sigv4-fetch package, which automatically signs fetch requests with SigV4 for a given AWS service and region.

What is AWS Signature Version 4?

AWS Signature Version 4 (SigV4) is a process for adding authentication information to AWS API requests sent over HTTP. For security reasons, most requests to AWS must be signed with an access key, which consists of an access key ID and a secret access key (your AWS credentials).

The SigV4 signing process involves creating a canonical request based on the HTTP request details, calculating a signature using your AWS credentials, and adding this signature to the request as an Authorization header. AWS then replicates this process and verifies the signature, granting or denying access accordingly.

For a more detailed explanation of SigV4, refer to my previous article or the AWS documentation.

Sign All Requests

The aws-sigv4-fetch package aims to simplify the SigV4 signing process for modern JavaScript applications. It exports a single function, createSignedFetcher, which returns a fetch function that automatically signs HTTP requests with SigV4 for the specified AWS service and region.

Here's an example usage:

import { createSignedFetcher } from 'aws-sigv4-fetch'; const signedFetch = createSignedFetcher({ service: 'execute-api', region: 'eu-west-1' }); const url = '<https://restapi.execute-api.eu-west-1.amazonaws.com/foo/bar>'; const response = await signedFetch(url); const data = await response.json();

In this example, we create a signedFetch function that automatically signs requests to API Gateway in the eu-west-1 region. We can then use this function like a regular fetch, passing in the URL and request options. The aws-sigv4-fetch package will handle the SigV4 signing process behind the scenes, adding the necessary Authorization header to the request.

The createSignedFetcher function accepts an optional fetch argument, allowing you to pass in a custom fetch implementation (e.g., a polyfill like cross-fetch). If no fetch is provided, it defaults to the native fetch function which is available in Node.js since v18.

ESM and CommonJS Support

The aws-sigv4-fetch package is available on npm and can be installed with your preferred package manager:

npm install aws-sigv4-fetch

The package supports both ES Modules and CommonJS, so you can import or require it as needed:

// ESM import { createSignedFetcher } from 'aws-sigv4-fetch'; // CommonJS const { createSignedFetcher } = require('aws-sigv4-fetch'); const signedFetch = createSignedFetcher({ service: 'appsync', region: 'eu-west-1' });

Integration with GraphQL Libraries

The aws-sigv4-fetch package can be integrated into GraphQL libraries like graphql-request. For example, you can pass the signedFetch function as the custom fetch option:

import { createSignedFetcher } from 'aws-sigv4-fetch'; import { GraphQLClient } from 'graphql-request'; const signedFetch = createSignedFetcher({ service: 'appsync', region: 'eu-west-1' }); const client = new GraphQLClient('<https://graphqlapi.appsync-api.eu-west-1.amazonaws.com/graphql>', { fetch: signedFetch, }); const result = await client.request(query, variables);

With this setup, all GraphQL requests made through the client will be automatically signed with SigV4.

Contributions

I am in the process of adding E2E tests for AWS services. So far only API Gateway and IAM are covered by tests with real resources. If you are using a specific AWS service and want to make sure that aws-sigv4-fetch always works with that service, I would greatly appreciate your contribution. Of course, feedback and improvements are always welcome.