Calling AWS Lambda Function URLs with IAM Auth

In a previous article, I detailed the process of utilizing GitHub Actions and AWS CDK to deploy Python code to AWS Lambda, including the setup of function URLs. However, I encountered a challenge when attempting to call this Lambda function from another application written in Node.js. Despite extensive research, I struggled to find clear, working examples that addressed my specific use case. This led to a day-long journey of trial and error before I finally cracked the code.

One crucial lesson learned during this process was the importance of consistency in request signing: the body used in generating the signature must be identical to the body included in the actual request. This seemingly small detail proved to be a critical factor in successfully calling the Lambda function.

import { SignatureV4 } from '@smithy/signature-v4';
import { Sha256 } from '@aws-crypto/sha256-js';

app.get('/test', async (req, res) => {  
    const API_URL  = 'https://yourLambdaFunctionUrl/gogogo';
    const apiUrl = new URL(API_URL);
    console.log('apiUrl =>', apiUrl);

    const sigv4 = new SignatureV4({
      service: 'lambda',
      region: 'us-east-1',
      credentials: {
        accessKeyId: 'YourAccessKeyId',
        secretAccessKey: 'YourSecretAccessKey',
      },
      sha256: Sha256
    }); 

    const signed = await sigv4.sign({
      method: 'POST',
      hostname: apiUrl.hostname,
      path: apiUrl.pathname,
      protocol: apiUrl.protocol,
      headers: {
        'Content-Type': 'application/json',
        'Host': apiUrl.host
      },
      body: JSON.stringify({hi: 'hello'})
    });
    console.log('signed =>', signed);

    const requestOptions = {
      method: signed.method,
      headers: {
        'Content-Type': 'application/json',
        'X-Amz-Content-Sha256': signed.headers['x-amz-content-sha256'],
        'Authorization': signed.headers['authorization'],
        'X-Amz-Date': signed.headers['x-amz-date'],      
      },
      // headers: signed.headers,
      body: JSON.stringify({hi: 'hello'}),
      redirect: 'follow'
    };

    console.log('requestOptions =>', requestOptions);
    const response = await fetch(API_URL, requestOptions);

    const body = await response.json();
    console.log('body =>', body);

    return res.send({
      status: 200,
      message: body
    });
  });