Search code examples
amazon-web-servicesaws-lambdaaws-appsync

Manually sign AppSync URL to use in Lambda gives bad signature error


In a Lambda, I would like to sign my AppSync endpoint with aws-signature-v4 in order to use it for a mutation.

The URL generated seems to be ok but it gives me the following error when I try it:

{ "errors" : [ { "errorType" : "InvalidSignatureException", "message" : "The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details. etc... } ] }

Here is my lambda function

import { Context, Callback } from 'aws-lambda';
import { GraphQLClient } from 'graphql-request';

const v4 = require('aws-signature-v4');

export async function handle(event: any, context: Context, callback: Callback) {
  context.callbackWaitsForEmptyEventLoop = false;

  const url = v4.createPresignedURL(
    'POST',
    'xxxxxxxxxxxxxxxxx.appsync-api.eu-west-1.amazonaws.com',
    '/graphql',
    'appsync',
    'UNSIGNED-PAYLOAD',
    {
      key: 'yyyyyyyyyyyyyyyyyyyy',
      secret: 'zzzzzzzzzzzzzzzzzzzzz',
      region: 'eu-west-1'
    }
  );

  const mutation = `{
    FAKEviewProduct(title: "Inception") {
      productId
    }
  }`;

  const client = new GraphQLClient(url, {
    headers: {
      'Content-Type': 'application/graphql',
      action: 'GetDataSource',
      version: '2017-07-25'
    }
  });

  try {
    await client.request(mutation, { productId: 'jfsjfksldjfsdkjfsl' });
  } catch (err) {
    console.log(err);
    callback(Error());
  }

  callback(null, {});
}

I got my key and secret by creating a new user and Allowing him appsync:GraphQL action.

What am I doing wrong?


Solution

  • This is how I trigger an AppSync mutation using by making a simple HTTP-request, using axios.

    const AWS = require('aws-sdk');
    const axios = require('axios');
    
    exports.handler = async (event) => {    
        let result.data = await updateDb(event);
    
        return result.data;
    };
    
    function updateDb({ owner, thingName, key }){
        let req = new AWS.HttpRequest('https://xxxxxxxxxxx.appsync-api.eu-central-1.amazonaws.com/graphql', 'eu-central-1');
        req.method = 'POST';
        req.headers.host = 'xxxxxxxxxxx.appsync-api.eu-central-1.amazonaws.com';
        req.headers['Content-Type'] = 'multipart/form-data';
        req.body = JSON.stringify({
            "query":"mutation ($input: UpdateUsersCamsInput!) { updateUsersCams(input: $input){ latestImage uid name } }",
            "variables": {
                "input": {
                    "uid": owner,
                    "name": thingName,
                    "latestImage": key
                }
            }
        });
    
        let signer = new AWS.Signers.V4(req, 'appsync', true);
        signer.addAuthorization(AWS.config.credentials, AWS.util.date.getDate());
    
        return axios({
            method: 'post',
            url: 'https://xxxxxxxxxxx.appsync-api.eu-central-1.amazonaws.com/graphql',
            data: req.body,
            headers: req.headers
        });
    }
    

    Make sure to give the IAM-role your Lambda function is running as, permissions for appsync:GraphQL.