A Simple Way to Sign AWS Requests with Signature V4
Tags: aws, javascript, typescript, webdev
Published: 2024-03-27 13:08:40

In a [previous article](https://dev.to/aws-builders/solving-the-puzzle-lambda-function-urls-with-iam-authorization-and-cloudfront-custom-domains-55jo), 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`](https://www.npmjs.com/package/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](https://dev.to/aws-builders/solving-the-puzzle-lambda-function-urls-with-iam-authorization-and-cloudfront-custom-domains-55jo) or the [AWS documentation](https://docs.aws.amazon.com/IAM/latest/UserGuide/create-signed-request.html).

## 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:

```typescript
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:

```bash
npm install aws-sigv4-fetch
```

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

```ts
// 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`](https://github.com/jasonkuhrt/graphql-request). For example, you can pass the `signedFetch` function as the custom `fetch` option:

```typescript
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.

{% embed https://github.com/zirkelc/aws-sigv4-fetch %}