寻找如何通过 HTTP 请求从 NodeJS 应用程序访问 AWS Appsync GraphQL API 的指南。

huangapple go评论93阅读模式
英文:

Looking for a guide to access AWS Appsync Graphql api from NodeJS app via http request

问题

目前据我所知,没有一个指南可以详细介绍这个过程,因此我想找到一个可以由其他人扩展和评论的指南,这些人可能有额外的安全性“最佳实践”等方面的贡献。

英文:

There is currently, to my knowledge, no one guide that walks through this process so I'd like to find one that can be extended upon and commented on by others who may have extra security "best practices" and the likes to contribute.

答案1

得分: 1

AWS Appsync API access via IAM user in NodeJS

我们将涵盖以下内容

  • 配置本地环境以构建和部署到 Amplify
  • 配置 Amplify 后端应用程序
  • 向我们的应用程序添加 GraphQL API
  • 配置 AWS IAM 以获取访问 API 所需的适当权限
  • 配置我们的应用程序以允许 IAM 用户访问 API
  • 为从 NodeJS 发送到 API 的请求构建签名函数

这将覆盖在 AppSync 中设置 GraphQL API 的整个过程(使用 Amplify),以供外部 NodeJS 应用程序通过 AWS_IAM 进行消耗。

先决条件

在这里假设您已经设置了 AWS 帐户,如果不是这种情况,您需要前往以下链接进行设置:

要开始,您需要前往 AWS 的文档并设置本地机器以构建和部署 Amplify 后端。请注意,虽然可以从 Amplify Studio 中完成某些设置,但必须从 CLI 中完成设置 IAM 权限的配置。

开始:https://docs.amplify.aws/lib/project-setup/prereq/q/platform/js/

设置后端应用程序

一旦您设置好了,我们将创建一个新的应用程序,基本上是按照此指南的“初始化新后端”部分进行操作:

  mkdir amplify-api
  cd amplify-api
  amplify init

设置 API

接下来,我们将向我们的应用程序添加一个 API 并相应地配置它,因此回到我们的:

  amplify add api
  • 在提示选择配置选项时,您将要选择“GraphQL”,尽管理论上此指南也应该与 REST 完美配合使用。

  • 接下来,您需要编辑“授权模式”选项。默认情况下,这将设置为“API 密钥”,但我们需要将其更改为“IAM”,暂时不必担心设置任何其他授权类型。

  • 然后,您将选择“Blank Schema”并选择“是”以编辑该模式。这将在您预定义的编辑器中打开“schema.graphql”文件,我们将在其中添加一些代码,使其看起来像这样:

# 这个 "input" 配置了一个全局授权规则,以允许公开访问
# 此模式中的所有模型。了解有关授权规则的更多信息: https://docs.amplify.aws/cli/graphql/authorization-rules
input AMPLIFY { globalAuthRule: AuthRule = { allow: public } } # 仅供测试使用!

type Todo @model @auth(rules: [{ allow: private, provider: iam, operations: [read, create, update] }]) {
  id: ID!
  title: String!
  status: Boolean
}

您可以配置此模式以看起来符合您的喜好,主要是为了从 API 外部访问的每个模型,您需要确保“@auth”规则与上面的规则匹配,更新任何要包括/省略的操作。

现在,我们需要部署我们的 API,因此运行:

  amplify push

当出现“您确定要继续吗?”的提示时,选择“是”,至于随后的提示,只需选择“否”。

这将在 AWS 端执行多项操作,包括在 DynamoDB 中创建数据库,为我们之前设置的模式创建对应的表,并为所有这些资源分配权限。

设置我们的 IAM 用户

在浏览器中前往 AWS 帐户并导航到 IAM 部分,在这里我们将添加一个新用户“amplify-api-user” - 或者您喜欢的任何名称。

点击下一步,然后从 3 个选项中选择“直接附加策略”,然后点击右上角的“创建策略”。

将打开一个新窗口,您现在可以为此用户创建一个安全策略,限制其仅访问其所需的 AWS 资源。在我们的情况下,此策略将如下所示(使用 JSON 编辑器):

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "appsync:GraphQL"
            ],
            "Resource": "{APPSYNC_API_ARN}/*"
        }
    ]
}

要访问您的 {APPSYNC_API_ARN},您需要打开一个新窗口并前往 AWS 中的 AppSync。选择我们之前创建的 API“amplify-api”,然后在左侧选择“设置”。您应该看到一个名为“API ARN”的字段,复制该值并将其插入到上述策略中。它应该看起来像这样:

  arn:aws:appsync:ap-southeast-2:xxxxxxxxxxxxxx:apis/xxxxxxxxxxxxxxxxx

授予 IAM 用户在我们的应用程序中的权限

现在,能够通过我们的 IAM 用户访问 API的关键步骤来了。在我们的本地机器上的 amplify-api 文件夹中,我们应该有一个类似于这样的文件夹结构:

- amplify/
 | - backend/
   | - api/
     | - amplifyapi/
       | - build/
       | - schema.graphql
       | - ...
   | - types/
   | - backend-config.json
   | - ...
 | - hooks/
 | cli.json
 | ...
- src/
 | - aws-exports.js

在目录“/amplify-api/amplify/backend/api/amplifyapi/”中,我们需要添加一个名为“custom-roles.json”的新文件:

{
    "adminRoleNames": ["{AWS_IAM_ARN}"]
}

与之前的“APPSYNC_API_ARN”一样,我们需要获取我们

英文:

AWS Appsync API access via IAM user in NodeJS

What we'll cover

  • Setting up your local env to build and deploy to amplify
  • Setting up an Amplify backend app
  • Adding a graphql api to our app
  • Configuring AWS IAM with appropriate permissions to access our api
  • Configuring our app to allow the IAM user to access the api
  • Building a signing function for requests made to the api from NodeJS

This should cover the entire process of setting up a graphql api within AppSync (using Amplify), to be consumed by an external NodeJS app via AWS_IAM auth.

Prerequisites

It is assumed here that you already have an AWS account set up, if that is not the case, you will need to head over here to set one up:

To get started you'll need to head on over to this doc from AWS and set up your local machine to build and deploy an amplify backend. Note that while some of this setup can be done from the Amplify Studio, the config to setup IAM permissions must be done from the CLI.

Getting started: https://docs.amplify.aws/lib/project-setup/prereq/q/platform/js/

Setting up the Backend App

Once you're set up we'll create a new app, essentially following the "Initialize a new backend" section of this guide:

  mkdir amplify-api
  cd amplify-api

  amplify init

Setting up the API

Next we'll add an api to our app and configure it accordingly, so jumping back in our :

  amplify add api
  • When prompted to make configuration selections you'll want to select "Graphql", although in theory this guide should also work perfectly fine with REST as well.

  • Next you need to edit the "Authorization modes" option. By default this will be set to "API key" however we need to change this to "IAM", don't worry about setting up any other auth types for now.

  • You'll then choose "Blank Schema" and select "Yes" to edit that schema. This will open the "schema.graphql" file in your pre-defined editor, which we'll add some code to so that it looks like this:

# This "input" configures a global authorization rule to enable public access to
# all models in this schema. Learn more about authorization rules here: https://docs.amplify.aws/cli/graphql/authorization-rules
input AMPLIFY { globalAuthRule: AuthRule = { allow: public } } # FOR TESTING ONLY!

type Todo @model @auth(rules: [{ allow: private, provider: iam, operations: [read, create, update] }]) {
  id: ID!
  title: String!
  status: Boolean
}

You can configure this schema to look however you like, the main thing is for every model you wish to be able to access externally from the API, you need to ensure the "@auth" rules match the ones above, updating any operations you wish to include / omit.

We now need to deploy our API so run:

  amplify push

When greeted with " Are you sure you want to continue?" select "yes" and as for the other prompts that will follow this, you can just select "no".

This will do a number of things on the AWS end including creating a database in DynamoDB, setting up with the tables corresponding to our schema we set up before, and assigning permissions to all of these resources.

Setting up our IAM user

In your browser head over to your AWS account and navigate to the IAM section, here we'll add a new user "amplify-api-user" - or whatever you like.

Hit next and then from the 3 options, select "Attach policies directly", and then click on "Create Policy" in the top right.

A new window should be opened and you can now create a security policy for this user restricting their access to only the AWS resources they need. In our case this policy will look like this (using the JSON editor):

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "appsync:GraphQL"
            ],
            "Resource": "{APPSYNC_API_ARN}/*"
        }
    ]
}

To access your {APPSYNC_API_ARN} you'll need to open a new window and head over to AppSync within AWS. Select the api we created earlier "amplify-api" and then on the left select "settings". You should see a field titled "API ARN", copy this value and insert it in the policy above. It should look something like this:

  arn:aws:appsync:ap-southeast-2:xxxxxxxxxxxxxx:apis/xxxxxxxxxxxxxxxxx

Granting IAM User permissions in our app

Now comes a critical step in being able to access our API via our IAM user. In our amplify-api folder on our local machine we should have a folder structure that looks similar to this:

- amplify/
 | - backend/
   | - api/
     | - amplifyapi/
       | - build/
       | - schema.graphql
       | - ...
   | - types/
   | - backend-config.json
   | - ...
 | - hooks/
 | cli.json
 | ...
- src/
 | - aws-exports.js

In the directory "/amplify-api/amplify/backend/api/amplifyapi/" we need to add a new file called "custom-roles.json":

  {
    "adminRoleNames": ["{AWS_IAM_ARN}"]
  }

Like with the "APPSYNC_API_ARN" before, we need to grab the ARN id for our IAM user. So in your browser navigate to the users section of IAM, select your user and copy the ARN value which should look something like this:

  arn:aws:iam::xxxxxxxxxxx:user/amplify-api-user

Once this file has been added to our app we can once again push these changes:

  amplify push

Creating access keys for our IAM user

The final step in setting up the AWS side of things is to add access keys to our IAM user. We'll use these as the primary auth tokens when we make our requests later on.

Head over to the IAM section of AWS and select your user once again. Click on "Security credentials" and then scroll down to "Create access key". From the "Use cases", it doesn't matter what you select, but we'll just use "Third-party service". Add a dec if you like and you'll then be taken to a screen with your "Access key" and "Secret access key".

Store these in a safe place, or even download the .csv file then we're done.

Creating the request function

Finally we can now test out all that hard work, well, almost. We need to provide a way to sign requests made to our api using the access keys we just set up. This will be the code we set up on our external app running Nodejs to make the calls to our API.

The snippet below is a modified version of code found in these resources:

Without which this guide would not be possible so huge thanks to those authors!

First off you'll need to install a couple of packages, these libraries are used to create the authed object we will send in our fetch request to AWS:

  npm i @smithy/signature-v4 @smithy/protocol-http @aws-crypto/sha256-js

Once those are installed we can import them and begin building our request object:

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

const {
  API_URL,
  AWS_ACCESS_KEY_ID,
  AWS_SECRET_ACCESS_KEY
} = process.env;

const apiUrl = new URL(API_URL!)

const signer = new SignatureV4({
  service: 'appsync',
  region: 'ap-southeast-2',
  credentials: {
    accessKeyId: AWS_ACCESS_KEY_ID!,
    secretAccessKey: AWS_SECRET_ACCESS_KEY!
  },
  sha256: Sha256,
})

export const signedFetch = async (graphqlObject) => {

  if (!graphqlObject) return

  // set up the HTTP request
  const request = new HttpRequest({
    hostname: apiUrl.host,
    path: apiUrl.pathname,
    body: JSON.stringify(graphqlObject),
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      host: apiUrl.hostname
    },
  })

  const signedRequest = await signer.sign(request)

  const { headers, body, method } = await signedRequest

  const awsSignedRequest = await fetch(API_URL!, {
    headers,
    body,
    method
  }).then((res) => res.json())

  return awsSignedRequest
}

The variables being called from "process.env" pertain to the "access key" and "secret access key" we created earlier, the "API_URL" refers to our Graphql API url which we can grab from our API in Appsync under the "GraphQL endpoint" in settings.

Then to use this function to make a request to the Graphql API:

const MyGraphqlQuery =  {
  query: `
    query getTodos {
      listTodos {
        items {
          title
          status
        }
      }
    }
  `
}


const response = signedRequest(MyGraphqlQuery).then((res) => res)

This can all be wrapped up in a set of functions/files or split out however best suites your app structure.

huangapple
  • 本文由 发表于 2023年8月10日 11:00:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/76872395.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定