如何使用 IAM 在 AWS Lambda 中调用 AppSync?

Jos*_*e A 5 amazon-web-services aws-lambda graphql aws-appsync

我目前正在使用 AppSync 在 AWS Lambda 中实施订阅突变。我想使用 IAM 并避免使用任何其他类型的 AUTH 机制,因为我在 AWS 堆栈中调用它。不幸的是,我收到以下 403 错误:

(摘自 SQS 的 CloudWatch 日志)

 {
    "errorMessage": "Response not successful: Received status code 403",
    "name": "ServerError",
    "errorType": "UnrecognizedClientException",
    "message": "The security token included in the request is invalid."
 }
Run Code Online (Sandbox Code Playgroud)

我尝试遵循这些但无济于事,但我不知道我错过了什么:

这是我当前调用它的代码:

import AWS from "aws-sdk";
import { AWSAppSyncClient } from "aws-appsync";
import { Mutation, mutations } from "./mutations/";
import "cross-fetch/polyfill";

/**
 *
 */

AWS.config.update({
  region: Config.region,
});

export class AppSyncClient {
  client: AWSAppSyncClient<any>;
  constructor() {
    if (!env.APPSYNC_ENDPOINT) {
      throw new Error("APPSYNC_ENDPOINT not defined");
    }

    /**
     * We create the AppSyncClient with the AWS_IAM
     * authentication.
     */
    this.client = new AWSAppSyncClient({
      url: env.APPSYNC_ENDPOINT,
      region: Config.region,
      auth: {
        credentials: AWS.config.credentials!,
        type: "AWS_IAM",
      },
      disableOffline: true,
    });
  }

  /**
   * Sends a mutation on the AppSync Client
   * @param mutate The Mutation that will be sent with the variables.
   * @returns
   */
  sendMutation(mutate: Mutation) {
    const mutation = mutations[mutate.type] as any;
    const variables = mutate.variables;
    console.log("Sending the mutation");
    console.log("Variables is ", JSON.stringify(variables));
    return this.client.mutate({
      mutation,
      fetchPolicy: "network-only",
      variables,
    });
  }
}
Run Code Online (Sandbox Code Playgroud)

以下是 Lambda SQS 中的当前 IAM:

{
    "Statement": [
        {
            "Action": [
                "appsync:GraphQL"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:appsync:us-east-2:747936726382:apis/myapi"
            ]
        }
    ],
    "Version": "2012-10-17"
}
Run Code Online (Sandbox Code Playgroud)

我知道这不是 lambda 的 IAM 问题,因为我已尝试暂时授予其完全访问权限,但仍然收到 403 错误。

我还验证了 AppSync 已配置 IAM 权限(作为附加提供商)。

你们有什么想法吗?令我印象深刻的是,这是一个幽灵主题,配置参考如此之少。

Jos*_*e A 9

我终于成功了。我第三次重新阅读了Adrian Hall 的帖子,它确实让我找到了解决方案。

请注意,我安装了 AWS AppSync 客户端,该客户端不是必需的,但可以简化流程(否则您必须自己签署 URL。有关这一点,请参阅 Adrian Hall 的帖子)。

有以下几点:

  • 您需要通过包含其中之一来填充“fetch” cross-fetch(否则您将受到 AppSync 内部使用的 Apollo 客户端的不变违规的影响)。
  • 您需要将 lambda 的内部 IAM 凭证(我什至不知道其存在)传递到 AppSyncClient 的配置部分。
  • 您需要向 lambda 的 IAM 角色添加适当的权限,在本例中:["appsync:GraphQL"]针对操作。

这是一些代码:

这是 AppSync 代码。

// The code is written in TypeScript.
// https://adrianhall.github.io/cloud/2018/10/26/backend-graphql-trigger-appsync/
// https://www.edwardbeazer.com/using-appsync-client-from-lambda/
import { env } from "process";
import { Config, env as Env } from "../../../../shared";
// This is such a bad practice
import AWS from "aws-sdk";
import { AWSAppSyncClient } from "aws-appsync";
import { Mutation, mutations } from "./mutations/";
// Very important, otherwise it won't work!!! You'll have Invariant Violation 
// from Apollo Client.
import "cross-fetch/polyfill";

/**
 *
 */
AWS.config.update({
  region: Config.region,
  credentials: new AWS.Credentials(
    env.AWS_ACCESS_KEY_ID!,
    env.AWS_SECRET_ACCESS_KEY!,
    env.AWS_SESSION_TOKEN!
  ),
});

export class AppSyncClient {
  client: AWSAppSyncClient<any>;
  constructor() {
    // Your AppSync endpoint - The Full URL.
    if (!Env.APPSYNC_ENDPOINT) {
      throw new Error("APPSYNC_ENDPOINT not defined");
    }

    /**
     * We create the AppSyncClient with the AWS_IAM
     * authentication.
     */
    this.client = new AWSAppSyncClient({
      url: Env.APPSYNC_ENDPOINT,
      region: Config.region,
      auth: {
        credentials: AWS.config.credentials!,
        type: "AWS_IAM",
      },
      disableOffline: true,
    });
  }

  /**
   * Sends a mutation on the AppSync Client
   * @param mutate The Mutation that will be sent with the variables.
   * @returns
   */
  // The mutation is a object that holds the mutation in 
  // the `gql` tag. You can ommit this part. 
  sendMutation(mutate: Mutation) {
    const mutation = mutations[mutate.type] as any;
    const variables = mutate.variables;
    // This is the important part.
    return this.client.mutate({
      mutation,
      // Specify "no-cache" in the policy. 
      // network-only won't work.
      fetchPolicy: "no-cache",
      variables,
    });
  }
}

Run Code Online (Sandbox Code Playgroud)

我们需要在AppSync授权机制中启用IAM。是的,可以启用多个身份验证。我目前同时使用 OPEN_ID 和 IAM。

https://us-east-2.console.aws.amazon.com/appsync/home?region=us-east-2#/myappsync-id/v1/settings

在此输入图像描述

以下是执行 GQL 的 Lambda 的 IAM 策略:

{
    "Statement": [
        {
            "Action": [
                "appsync:GraphQL"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:appsync:us-east-2:747936726382:apis/ogolfgja65edlmhkcpp3lcmwli/*"
           
            ]
        }
    ],
    "Version": "2012-10-17"
}
Run Code Online (Sandbox Code Playgroud)

您可以通过以下方式进一步限制: arn:${Partition}:appsync:${Region}:${Account}:apis/${GraphQLAPIId}/types/${TypeName}/fields/${FieldName}

arn:aws:appsync:us-east-2:747936726382:apis/ogolfgja65edlmhkcpp3lcmwli/types/Mutation/field/myCustomField"

请注意,我们需要更好地限制这一点,因为我们目前授予它对 API 的完整访问权限。

在 .gql 文件(AppSync GraphQL 架构)中,将 @aws_iam 指令添加到用于发送订阅的突变,以限制来自前端的访问。

  type Mutation {
  addUsersMutationSubscription(
    input: AddUsersSagaResultInput!
  ): AddUsersSagaResult @aws_iam
}
Run Code Online (Sandbox Code Playgroud)