4
votes

I am having trouble calling an AppSync GraphQL query via a AWS Lambda function. I have been using the code in this article, specifically the latter part that uses IAM permissions: https://docs.amplify.aws/lib/graphqlapi/graphql-from-nodejs/q/platform/js#signing-a-request-from-lambda

const https = require("https");
const AWS = require("aws-sdk");
const urlParse = require("url").URL;
const appsyncUrl = process.env.API_MYAPP_GRAPHQLAPIENDPOINTOUTPUT;
const region = process.env.REGION;
const endpoint = new urlParse(appsyncUrl).hostname.toString();
const graphqlQuery = require("./query.js").query;

exports.handler = async (event) => {
  const req = new AWS.HttpRequest(appsyncUrl, region);

  req.method = "POST";
  req.path = "/graphql";
  req.headers.host = endpoint;
  req.headers["Content-Type"] = "application/json";
  req.body = JSON.stringify({
    query: graphqlQuery,
    operationName: "list",
  });

  const signer = new AWS.Signers.V4(req, "appsync", true);
  signer.addAuthorization(AWS.config.credentials, AWS.util.date.getDate());

  const data = await new Promise((resolve, reject) => {
    const httpRequest = https.request({ ...req, host: endpoint }, (result) => {
      result.on("data", (data) => {
        resolve(JSON.parse(data.toString()));
      });
    });

    httpRequest.write(req.body);
    httpRequest.end();
  });

  return {
    statusCode: 200,
    body: data,
  };
};

I am using the Amplify CLI. I used the CLI to create the function and ensure it is given access to the GraphQL API.

The specific error I get in the Lambda is:

      {
  "statusCode": 200,
  "body": {
    "errors": [
      {
        "errorType": "UnauthorizedException",
        "message": "Permission denied"
      }
    ]
  }
}

GraphQL is set-up to use Cognito user pool as authentication and I've added IAM as a secondary authentication mechanism via the Amplify CLI. The AWS GraphQL console shows that I have Cognito as my primary authentication mechanism and IAM as secondary.

The Lambda function appears to be permissioned okay as it shows 4 resources (create, update, delete, read) corresponding to the API and Allow: appsync:GraphQL as the Action.

If I run the function locally using amplify mock function myfunction then it executes fine and the result of the GraphQL query is returned correctly.

I can also run the same query successfully via the AppSync UI when I choose IAM as the authentication mechanism.

The table I am accessing is defined in my schema.graphql as:

 type Business
  @model
  @auth(
    rules: [
      { allow: owner }
      { allow: groups, groups: ["Admin"] }
      { allow: private, provider: iam }
    ]
  ) {
  id: ID!
  owner: String!
  name: String!
  emailSuffix: String!
  shortCode: String!
}

I have removed the Auth from the model and that doesn't make a difference.

I've deleted the function and re-created it in case the permissions had somehow got messed up.

So I don't think it is the code, seems to be a permissions error. I'm at a loss as to where the issue could be

UPDATE I have amended the permissions policy inside IAM Manager for the amplify-lambda-execution policy and that appears to have fixed the issue.

The permissions policy initially added by Amplify was of the form:

arn:aws:appsync:MYREGION:MYID:apis/MYAPIID/types/create/*
arn:aws:appsync:MYREGION:MYID:apis/MYAPIID/types/read/*
arn:aws:appsync:MYREGION:MYID:apis/MYAPIID/types/edit/*
arn:aws:appsync:MYREGION:MYID:apis/MYAPIID/types/delete/*

amending this to:

arn:aws:appsync:MYREGION:MYID:apis/MYAPIID/types/*/fields/* 
arn:aws:appsync:MYREGION:MYID:apis/MYAPIID

allows the lambda function to execute and successfully execute the GraphQL query on my table. So it appears to be a problem with the permissions that Amplify adds to the function. Manually overwriting these isn't a great solution.

1

1 Answers

0
votes

The problem started after installed - amplify cli "4.45.2". The old code for autogenerated permissions in the lambda cloudformation template looked like this.

            {
          "Effect": "Allow",
          "Action": [
            "appsync:Create*",
            "appsync:StartSchemaCreation",
            "appsync:GraphQL",
            "appsync:Get*",
            "appsync:List*",
            "appsync:Update*",
            "appsync:Delete*"
          ],
          "Resource": [
            {
              "Fn::Join": [
                "",
                [
                  "arn:aws:appsync:",
                  {
                    "Ref": "AWS::Region"
                  },
                  ":",
                  {
                    "Ref": "AWS::AccountId"
                  },
                  ":apis/",
                  {
                    "Ref": "myApiGraphQLAPIIdOutput"
                  },
                  "/*"
                ]
              ]
            }
          ]
        }

But after I moved to amplify cli "4.45.2" and give lambda permissions for appsync. It generated :

    {
          "Effect": "Allow",
          "Action": [
            "appsync:GraphQL"
          ],
          "Resource": [
            {
              "Fn::Join": [
                "",
                [
                  "arn:aws:appsync:",
                  {
                    "Ref": "AWS::Region"
                  },
                  ":",
                  {
                    "Ref": "AWS::AccountId"
                  },
                  ":apis/",
                  {
                    "Ref": "myApiGraphQLAPIIdOutput"
                  },
                  "/types/create/*"
                ]
              ]
            },
            {
              "Fn::Join": [
                "",
                [
                  "arn:aws:appsync:",
                  {
                    "Ref": "AWS::Region"
                  },
                  ":",
                  {
                    "Ref": "AWS::AccountId"
                  },
                  ":apis/",
                  {
                    "Ref": "myApiGraphQLAPIIdOutput"
                  },
                  "/types/read/*"
                ]
              ]
            },
            {
              "Fn::Join": [
                "",
                [
                  "arn:aws:appsync:",
                  {
                    "Ref": "AWS::Region"
                  },
                  ":",
                  {
                    "Ref": "AWS::AccountId"
                  },
                  ":apis/",
                  {
                    "Ref": "myApiGraphQLAPIIdOutput"
                  },
                  "/types/update/*"
                ]
              ]
            }
          ]
        }


enter code here

And I got error "permission denied" when trying to reach appsync from lambda.

I think the new cli has some bug when generating appsync permissions for lambda.

I manually moved back on the old generated code in the lambda cloudformation file and it worked.