

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

# GraphQL API を保護するための認可と認証の設定
<a name="security-authz"></a>

AWS AppSync は、GraphQL APIs を保護するため、API キー、Lambda、IAM、OpenID Connect、および Cognito ユーザープールの認可タイプを提供します。各オプションは、異なるセキュリティ方法を提供します。

1. **API キー認可**: 認証されていない API のスロットリングを制御し、シンプルなセキュリティオプションを提供します。

1. **Lambda 認可**: 関数の入力と出力を詳細に説明するカスタム認可ロジックを有効にします。

1. **IAM 認可**: AWS署名バージョン 4 の署名プロセスを使用し、IAM ポリシーによるきめ細かなアクセスコントロールを可能にします。

1. **OpenID Connect 認可**: ユーザー認証のために OIDC 準拠のサービスと統合します。

1. **Cognito ユーザープール**: Cognito のユーザー管理機能を使用して、グループベースのアクセスコントロールを実装します。

## 認可タイプ
<a name="authorization-types"></a>

 AWS AppSync GraphQL API を操作するためにアプリケーションを認可するには、5 つの方法があります。 AWS AppSync API または CLI コールで次のいずれかの認可タイプ値を指定することで、使用する認可タイプを指定します。
+   
** `API_KEY` **  
API キーを使用する場合。
+   
** `AWS_LAMBDA` **  
 AWS Lambda 関数を使用する場合。
+   
** `AWS_IAM` **  
 AWS Identity and Access Management ([IAM](https://aws.amazon.com/iam/)) アクセス許可を使用する場合。
+   
** `OPENID_CONNECT` **  
OpenID Connect プロバイダーを使用する場合。
+   
** `AMAZON_COGNITO_USER_POOLS` **  
Amazon Cognito ユーザープールを使用する場合。

これらの基本的な承認タイプは、ほとんどの開発者に有効です。より高度なユースケースでは、コンソール、CLI、 AWS CloudFormationを使用してさらに承認モードを追加できます。追加の認可モードの場合、 AWS AppSync は上記の値 (、、、`API_KEY``AWS_LAMBDA``AWS_IAM``OPENID_CONNECT`、) を取得する認可タイプを提供します`AMAZON_COGNITO_USER_POOLS`。

メインまたはデフォルトの承認タイプとして`API_KEY`、`AWS_LAMBDA`、または `AWS_IAM` を指定した場合、それを追加の承認モードの 1 つとして再び指定することはできません。同様に、追加の承認モード間で `API_KEY`、`AWS_LAMBDA`、または `AWS_IAM` を重複して使用することはできません。複数の Amazon Cognito ユーザープールと OpenID Connect プロバイダーを使用できます。ただし、デフォルトの承認モードと追加の承認モード間で Amazon Cognito ユーザープールまたは OpenID Connect プロバイダーを重複して使用することはできません。Amazon Cognito ユーザープールまたは OpenID Connect プロバイダーに異なる複数のクライアントを設定する場合、対応する正規表現を使用できます。

API 設定に変更を保存すると、 は変更の伝達 AWS AppSync を開始します。設定変更が反映されるまで、 は AWS AppSync 引き続き以前の設定のコンテンツを提供します。設定変更が伝播されると、 AWS AppSync はすぐに新しい設定に基づいてコンテンツの提供を開始します。 AWS AppSync が API の変更を伝達している間、API が以前の設定または新しい設定に基づいてコンテンツを提供しているかどうかを判断できません。

## API\$1KEY 認可
<a name="api-key-authorization"></a>

認証されていない API は、認証された API よりも厳格なスロットリングが必要になります。認証されていない GraphQL エンドポイントに対してスロットリングを制御する方法の 1 つは API キーを使用します。API キーは、認証されていない GraphQL エンドポイントを作成するときに AWS AppSync サービスによって生成されるアプリケーション内のハードコードされた値です。コンソール、CLI、または [AWS AppSync API リファレンス](https://docs.aws.amazon.com/appsync/latest/APIReference/)から API キーを更新できます。

------
#### [ Console ]

1. にサインイン AWS マネジメントコンソール し、[AppSync コンソール](https://console.aws.amazon.com/appsync/)を開きます。

   1. **API ダッシュボード**で、GraphQL API を選択します。

   1. **サイドバー**で **[設定]** を選択します。

1. **デフォルト認可モード**で、**API キー **を選択します。

1. **[API キー]** テーブルで、**[API キーの追加]** を選択します。

   新しい API キーがテーブルで生成されます。

   1. 古い API キーを削除するには、テーブル内で API キーを選択し、**[削除]** を選択します。

1. ページの最下部で **[保存]** をクリックします。

------
#### [ CLI ]

1. まだ設定していない場合は、 CLI AWS へのアクセスを設定します。詳細については、「[設定の基本](https://docs.aws.amazon.com//cli/latest/userguide/cli-configure-quickstart.html)」を参照してください。

1. [https://awscli.amazonaws.com/v2/documentation/api/latest/reference/appsync/update-graphql-api.html](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/appsync/update-graphql-api.html) コマンドを実行して GraphQL API オブジェクトを作成します。

   この特定のコマンドには次の 2 つのパラメータを入力する必要があります。

   1. GraphQL API の `api-id`。

   1. API の新しい `name`。同じ `name` を使用できます。

   1. `API_KEY` となる `authentication-type`。
**注記**  
`Region` など、設定する必要のあるパラメータは他にもありますが、通常はデフォルトで CLI 設定値になります。

   コマンドの例は、次のようになります。

   ```
   aws appsync update-graphql-api --api-id abcdefghijklmnopqrstuvwxyz --name TestAPI --authentication-type API_KEY
   ```

   出力は CLI に返されます。以下に JSON の例を示します。

   ```
   {
       "graphqlApi": {
           "xrayEnabled": false,
           "name": "TestAPI",
           "authenticationType": "API_KEY",
           "tags": {},
           "apiId": "abcdefghijklmnopqrstuvwxyz",
           "uris": {
               "GRAPHQL": "https://s8i3kk3ufhe9034ujnv73r513e.appsync-api.us-west-2.amazonaws.com/graphql",
               "REALTIME": "wss://s8i3kk3ufhe9034ujnv73r513e.appsync-realtime-api.us-west-2.amazonaws.com/graphql"
           },
           "arn": "arn:aws:appsync:us-west-2:348581070237:apis/abcdefghijklmnopqrstuvwxyz"
       }
   }
   ```

------

API キーは、最大 365 日間有効に設定可能で、該当日からさらに最大 365 日、既存の有効期限を延長できます。API キーは、パブリック API の公開が安全であるユースケース、または開発目的での使用が推奨されます。

クライアントでは、API キーをヘッダー `x-api-key` で指定します。

たとえば、`API_KEY` が `'ABC123'` である場合、次のように `curl` 経由で GraphQL クエリを送信できます。

```
$ curl -XPOST -H "Content-Type:application/graphql" -H "x-api-key:ABC123" -d '{ "query": "query { movies { id } }" }' https://YOURAPPSYNCENDPOINT/graphql
```

## AWS\$1LAMBDA 認可
<a name="aws-lambda-authorization"></a>

 AWS Lambda 機能を使用して、独自の API 認可ロジックを実装できます。Lambda 関数をプライマリオーソライザーまたはセカンダリオーソライザーに使用できますが、API ごとに Lambda 認可関数は 1 つしかありません。Lambda 関数を認可に使用する場合、次のことが適用されます。
+ API で `AWS_LAMBDA` および `AWS_IAM` 認可モードが有効になっている場合、SigV4 署名を `AWS_LAMBDA` 認可トークンとして使用することはできません。
+ API で `AWS_LAMBDA` および `OPENID_CONNECT` 認可モードまたは `AMAZON_COGNITO_USER_POOLS` 認可モードが有効になっている場合、OIDC 署名を `AWS_LAMBDA` 認可トークンとして使用することはできません。OIDC トークンはベアラースキームにすることができます。
+ Lambda 関数は、リゾルバーに対して 5MB を超えるコンテキストデータを返してはいけません。

例えば、認可トークン が `'ABC123'` である場合、次のように curl 経由で GraphQL クエリを送信できます。

```
$ curl -XPOST -H "Content-Type:application/graphql" -H "Authorization:ABC123" -d '{ "query":
         "query { movies { id } }" }' https://YOURAPPSYNCENDPOINT/graphql
```

Lambda 関数は、各クエリまたはミューテーションの前に呼び出されます。戻り値は API ID と認証トークンに基づいてキャッシュできます。Lambda オーソライザーのレスポンスが 1,048,576 バイト未満の場合、 AWS AppSync は後続のリクエストのレスポンスをキャッシュします。Lambda オーソライザーのレスポンスが 1,048,576 バイト以上の場合、 AWS AppSync はレスポンスをキャッシュせず、受信リクエストごとに Lambda オーソライザーを呼び出します。パフォーマンスを最適化し、Lambda 呼び出しコストを最小限に抑えるには、Lambda オーソライザーのレスポンスを 1,048,576 バイトに制限することをお勧めします。デフォルトでは、キャッシュは有効になっていませんが、これは API レベルで、または関数の戻り値の `ttlOverride` の値を設定することで有効にすることができます。

必要に応じて、関数が呼び出される前に認可トークンを検証する正規表現を指定できます。これらの正規表現は、関数が呼び出される前に、認可トークンが正しい形式であることを検証するために使用されます。この正規表現と一致しないトークンを使用したリクエストは、自動的に拒否されます。

認可に使用される Lambda 関数には、 `appsync.amazonaws.com` がそれらを AWS AppSync 呼び出すことができるように、 のプリンシパルポリシーを適用する必要があります。このアクションは AWS AppSync コンソールで自動的に行われます。 AWS AppSync コンソールはポリシーを削除*しません*。Lambda 関数にポリシーをアタッチする方法の詳細については、「 AWS Lambda デベロッパーガイド」の[「リソースベースのポリシー](https://docs.aws.amazon.com/lambda/latest/dg/access-control-resource-based.html#permissions-resource-serviceinvoke)」を参照してください。

指定した Lambda 関数は次の形状のイベントを受け取ります。

```
{
    "authorizationToken": "ExampleAUTHtoken123123123",
    "requestContext": {
        "apiId": "aaaaaa123123123example123",
        "accountId": "111122223333",
        "requestId": "f4081827-1111-4444-5555-5cf4695f339f",
        "queryString": "mutation CreateEvent {...}\n\nquery MyQuery {...}\n",
        "operationName": "MyQuery",
        "variables": {}
    }
    "requestHeaders": {
        application request headers
    }
}
```

`event` オブジェクトには、アプリケーションクライアントから へのリクエストで送信されたヘッダーが含まれています AWS AppSync。

認可関数は少なくとも を返す必要があります。これは`isAuthorized`、リクエストが認可されているかどうかを示すブール値です。 は、Lambda 認可関数から返された次のキー AWS AppSync を認識します。

**注記**  
WebSocket 接続オペレーション`requestContext`の `operationName`の の値は、 によって`DeepDish:Connect` AWS AppSync 「」に設定されます。

### 関数のリスト
<a name="aws-lambda-authorization-list"></a>

`isAuthorized` (boolean、必須)  
GraphQL API への呼び出しをおこなうために `authorizationToken` の値が認証されるかどうかを示すブール値。  
この値が true の場合、GraphQL API の実行が継続されます。この値が false の場合、`UnauthorizedException` が生成されます。

`deniedFields` (文字列のリスト、オプション)  
リゾルバーから値が返された場合でも、そのリストは強制的に `null` に変更されます。  
各項目は、`arn:aws:appsync:us-east-1:111122223333:apis/GraphQLApiId/types/TypeName/fields/FieldName` の形式、または `TypeName.FieldName` の短い形式の完全修飾フィールド ARN です。2 つの API が Lambda 関数のオーソライザーを共有し、2 つの API の間に共通の型とフィールドの間にあいまいさがある可能性がある場合は、完全な ARN 形式を使用する必要があります。

`resolverContext` (JSON オブジェクト、オプション)  
リゾルバーテンプレート内の `$ctx.identity.resolverContext` として可視化されるJSON オブジェクト。たとえば、リゾルバーによって次の構造体が返されたとします。  

```
{
  "isAuthorized":true
  "resolverContext": {
    "banana":"very yellow",
    "apple":"very green" 
  }
}
```
リゾルバーテンプレート内の値 `ctx.identity.resolverContext.apple` は「`very green`」になります。`resolverContext` オブジェクト はキーと値のペアのみをサポートします。ネストされたキーはサポートされません。  
この JSON オブジェクトの合計サイズは 5 MB を超えないようにしてください。

`ttlOverride` (integer、オプション)  
応答をキャッシュする秒数。値が返されない場合は、API からの値が使用されます。これが 0 の場合、応答はキャッシュされません。

Lambda オーソライザーの標準タイムアウトは 10 秒ですが、ピークトラフィック条件下ではより早くタイムアウトすることがあります。API のパフォーマンスをスケーリングするために、できるだけ短い時間 (1 秒未満) で実行する関数を設計することをお勧めします。

 AWS AppSync APIsは、単一の認証 Lambda 関数を共有できます。クロスアカウントオーソライザーの使用は許可されていません。

複数の API 間で認可関数を共有する場合は、短い形式のフィールド名 (`typename.fieldname`) が誤ってフィールドを隠すことがあります。`deniedFields` のフィールドを明確化するには、明確なフィールド ARN を `arn:aws:appsync:region:accountId:apis/GraphQLApiId/types/typeName/fields/fieldName` の形式で指定できます。

 AWS AppSyncで Lambda 関数をデフォルトの認可モードとして追加するには、

------
#### [ Console ]

1.  AWS AppSync コンソールにログインし、更新する API に移動します。

1. API の設定ページに移動します。

   API レベルの認可を**AWS Lambda**に変更します。

1. API コールを承認する AWS リージョン と Lambda ARN を選択します。
**注記**  
適切なプリンシパルポリシーが自動的に追加され、 AWS AppSync Lambda 関数を呼び出します。

1. 必要に応じて、レスポンス TTL とトークン検証の正規表現を設定します。

------
#### [ AWS CLI ]

1. 使用中の Lambda 関数に次のポリシーをアタッチします。

   ```
   aws lambda add-permission --function-name "my-function" --statement-id "appsync" --principal appsync.amazonaws.com --action lambda:InvokeFunction --output text 
   ```
**重要**  
関数のポリシーを単一の GraphQL API にロックする場合は、次のコマンドを実行できます。  

   ```
   aws lambda add-permission --function-name “my-function” --statement-id “appsync” --principal appsync.amazonaws.com --action lambda:InvokeFunction --source-arn “<my AppSync API ARN>” --output text
   ```

1. 指定された Lambda 関数 ARN をオーソライザーとして使用するように AWS AppSync API を更新します。

   ```
   aws appsync update-graphql-api --api-id example2f0ur2oid7acexample --name exampleAPI --authentication-type AWS_LAMBDA --lambda-authorizer-config authorizerUri="arn:aws:lambda:us-east-2:111122223333:function:my-function"
   ```
**注記**  
トークンの正規表現など、他の設定オプションを含めることもできます。

------

次の例では、Lambda 関数が AWS AppSync 認可メカニズムとして使用されたとき、その Lambda 関数が持つさまざまな認証状態および認証失敗状態を示しています。

```
def handler(event, context):
  # This is the authorization token passed by the client
  token = event.get('authorizationToken')
  # If a lambda authorizer throws an exception, it will be treated as unauthorized. 
  if 'Fail' in token:
    raise Exception('Purposefully thrown exception in Lambda Authorizer.')

  if 'Authorized' in token and 'ReturnContext' in token:
    return {
      'isAuthorized': True,
      'resolverContext': {
        'key': 'value'
      }
    }

  # Authorized with no f
  if 'Authorized' in token:
    return {
      'isAuthorized': True
    }
  # Partial authorization
  if 'Partial' in token:
    return {
      'isAuthorized': True,
      'deniedFields':['user.favoriteColor']
    }
  if 'NeverCache' in token:
    return {
      'isAuthorized': True,
      'ttlOverride': 0
    }
  if 'Unauthorized' in token:
    return {
      'isAuthorized': False
    }
  # if nothing is returned, then the authorization fails. 
  return {}
```

### SigV4 と OIDC トークンの認可制限を回避する
<a name="aws-lambda-authorization-create-new-auth-token"></a>

以下の方法を使用すると、特定の認可モードが有効になっている場合に SigV4 署名または OIDC トークンを Lambda 認可トークンとして使用できないという問題を回避できます。

 AWS AppSyncの API に対して `AWS_IAM` および `AWS_LAMBDA` 認可モードが有効になっているとき、SigV4 署名を Lambda 認可トークンとして使用する場合は、次の操作を行います。
+ 新しい Lambda 認可トークンを作成するには、SigV4 署名にランダムなサフィックスおよび/またはプレフィックスを追加します。
+ 元の SigV4 署名を取得するには、Lambda 認可トークンからランダムなプレフィックスおよび/またはサフィックスを削除して、Lambda 関数を更新します。次に、元の SigV4 署名を認証に使用します。

の AWS AppSync API で認可モードまたは `AMAZON_COGNITO_USER_POOLS`および `OPENID_CONNECT`認可モードが有効になっているときに、OIDC トークンを Lambda `AWS_LAMBDA`認可トークンとして使用する場合は、以下を実行します。
+ 新しい Lambda 認可トークンを作成するには、OIDC トークンにランダムなサフィックスおよび/またはプレフィックスを追加します。Lambda 認可トークンにはベアラースキームプレフィックスを含めないでください。
+ 元の OIDC トークンを取得するには、Lambda 認可トークンからランダムなプレフィックスおよび/またはサフィックスを削除して、Lambda 関数を更新します。次に、元の OIDC トークンを認証に使用します。

## AWS\$1IAM 認可
<a name="aws-iam-authorization"></a>

この承認タイプでは、GraphQL API に対して [AWS 署名バージョン 4 署名プロセス](https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html)を使用する必要があります。Identity and Access Management ([IAM](https://aws.amazon.com/iam/)) アクセスポリシーをこの承認タイプに関連付けることができます。アクセスキー (アクセスキー ID とシークレットアクセスキーで構成) または Amazon Cognito フェデレーティッドアイデンティティによって提供される有効期限の短い、一時的な認証情報を使用して、アプリケーションでこの関連付けを活用します。

すべてのデータオペレーションを実行できるアクセス権限を持つロールが必要な場合。

------
#### [ JSON ]

****  

```
{
   "Version":"2012-10-17",		 	 	 
   "Statement": [
      {
         "Effect": "Allow",
         "Action": [
            "appsync:GraphQL"
         ],
         "Resource": [
            "arn:aws:appsync:us-west-2:123456789012:apis/YourGraphQLApiId/*"
         ]
      }
   ]
}
```

------

AppSync コンソールのメイン API リストページから、使用する API の名前の直下で、`YourGraphQLApiId` を確認できます。CLI `aws appsync list-graphql-apis` を使用して取得することもできます。

特定の GraphQL オペレーションのみにアクセスを制限するには、ルートの `Query`、`Mutation`、`Subscription` の各フィールドに対してこれを実行します。

------
#### [ JSON ]

****  

```
{
   "Version":"2012-10-17",		 	 	 
   "Statement": [
      {
         "Effect": "Allow",
         "Action": [
            "appsync:GraphQL"
         ],
         "Resource": [
            "arn:aws:appsync:us-west-2:123456789012:apis/YourGraphQLApiId/types/Query/fields/<Field-1>",
            "arn:aws:appsync:us-west-2:123456789012:apis/YourGraphQLApiId/types/Query/fields/<Field-2>",
            "arn:aws:appsync:us-west-2:123456789012:apis/YourGraphQLApiId/types/Mutation/fields/<Field-1>",
            "arn:aws:appsync:us-west-2:123456789012:apis/YourGraphQLApiId/types/Subscription/fields/<Field-1>"
         ]
     }
   ]
}
```

------

たとえば、以下のスキーマがあり、すべての投稿を取得するアクセスを制限する場合。

```
schema {
   query: Query
   mutation: Mutation
}

type Query {
   posts:[Post!]!
}

type Mutation {
   addPost(id:ID!, title:String!):Post!
}
```

ロール (Amazon Cognito ID プールなどにアタッチできる) に対応する IAM ポリシーは次のようになります。

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
            "appsync:GraphQL"
            ],
            "Resource": [
                "arn:aws:appsync:us-west-2:123456789012:apis/YourGraphQLApiId/types/Query/fields/posts"
            ]
        }
    ]
}
```

------

## OPENID\$1CONNECT 認可
<a name="openid-connect-authorization"></a>

この認可タイプは、OIDC 準拠サービスによって提供される [OpenID Connect](https://openid.net/specs/openid-connect-core-1_0.html) (OIDC) トークンを適用します。アプリケーションは、アクセス制御に対して、使用する OIDC プロバイダーによって定義されたユーザーと権限を活用できます。

発行者 URL は、 AWS AppSync に提供する唯一の必須設定値です (例: `https://auth.example.com`)。この URL は HTTPS 経由でアドレス可能である必要があります。 AWS AppSync は発行者 URL `/.well-known/openid-configuration` に を追加し、OpenID Connect Discovery 仕様`https://auth.example.com/.well-known/openid-configuration`に従って で OpenID 設定を見つけます。 [OpenID ](https://openid.net/specs/openid-connect-discovery-1_0.html) この URL で [RFC5785](https://tools.ietf.org/html/rfc5785) 準拠の JSON ドキュメントを取得するものとします。この JSON ドキュメントには、署名`jwks_uri`キーを含む JSON Web Key Set (JWKS) ドキュメントを指すキーが含まれている必要があります。 AWS AppSync では、JWKS に `kty`および の JSON フィールドが含まれている必要があります`kid`。

AWS AppSync は、さまざまな署名アルゴリズムをサポートしています。


| 署名アルゴリズム | 
| --- | 
| RS256 | 
| RS384 | 
| RS512 | 
| PS256 | 
| PS384 | 
| PS512 | 
| HS256 | 
| HS384 | 
| HS512 | 
| ES256 | 
| ES384 | 
| ES512 | 

RSA アルゴリズムを使用することをお勧めします。プロバイダーによって発行されたトークンに、トークンが発行された時刻 (`iat`) が含まれている必要があり、認証された時刻 (`auth_time`) が含まれる場合があります。発行時刻の TTL 値 (`iatTTL`) および認証時刻の TTL 値 (`authTTL`) を、追加の検証用に OpenID Connect 設定に入力できます。使用するプロバイダーが複数のアプリケーションを認可している場合は、クライアント ID で認可するために使用される正規表現を (`clientId`) も入力できます。`clientId` が OpenID Connect 設定に存在する場合、 AWS AppSync は、 がトークンの `aud`または クレームのいずれかと一致する`clientId`ように要求することで`azp`、クレームを検証します。

複数のクライアント ID を検証するには、正規表現で「or」であるパイプライン演算子 ("\$1") を使用します。例えば、OIDC アプリケーションに 4 つのクライアントがあり、そのクライアント ID が 0A1S2D、1F4G9H、1J6L4B、6GS5MG などである場合、最初の 3 つのクライアント ID のみを検証するには、クライアント ID フィールドに「1F4G9H\$11J6L4B\$16GS5MG」と入力します。

API が複数の認可タイプで設定されている場合、 AWS AppSync はリクエストヘッダーから JWT トークンに存在する発行者 (iss claim) を API 設定で指定された発行者 URL と比較することで検証します。ただし、API が OPENID\$1CONNECT 認可のみを使用して設定されている場合、 AWS AppSync はこの発行者 URL 検証ステップをスキップします。

## AMAZON\$1COGNITO\$1USER\$1POOLS 認可
<a name="amazon-cognito-user-pools-authorization"></a>

この認可タイプでは、Amazon Cognito ユーザープールによって提供される OIDC トークンが使用されます。アプリケーションは、別のアカウントのユーザープールとユーザープールの両方のユーザーとグループを活用し AWS 、これらを GraphQL フィールドに関連付けてアクセスを制御できます。

Amazon Cognito ユーザープールを使用する場合、ユーザーが属するグループを作成できます。この情報は、アプリケーションが GraphQL オペレーションを送信するときに認可ヘッダーで AWS AppSync に送信する JWT トークンにエンコードされます。スキーマで GraphQL ディレクティブを使用して、フィールドでどのグループがどのリゾルバーを起動できるのかを制御します。したがってカスタマーのアクセスを細かく制御できます。

たとえば、以下の GraphQL スキーマがあるとします。

```
schema {
   query: Query
   mutation: Mutation
}

type Query {
   posts:[Post!]!
}

type Mutation {
   addPost(id:ID!, title:String!):Post!
}
...
```

Amazon Cognito ユーザープールに 2 つのグループ (bloggers と readers) があり、readers が新しいエントリを追加できないように制限する場合、スキーマは次のようになります。

```
schema {
   query: Query
   mutation: Mutation
}
```

```
type Query {
   posts:[Post!]!
   @aws_auth(cognito_groups: ["Bloggers", "Readers"])
}

type Mutation {
   addPost(id:ID!, title:String!):Post!
   @aws_auth(cognito_groups: ["Bloggers"])
}
...
```

`@aws_auth` ディレクティブは、個別のアクセス許可/拒否戦略をデフォルトにする場合に省略できることに注意してください。コンソールまたは以下の CLI コマンド経由で GraphQL API を作成するときに、ユーザープール設定で許可/拒否戦略を指定できます。

```
$ aws appsync --region us-west-2 create-graphql-api --authentication-type AMAZON_COGNITO_USER_POOLS  --name userpoolstest --user-pool-config '{ "userPoolId":"test", "defaultEffect":"ALLOW", "awsRegion":"us-west-2"}'
```

## 追加の承認モードの使用
<a name="using-additional-authorization-modes"></a>

追加の認可モードを追加すると、 AWS AppSync GraphQL API レベル (`GraphqlApi`オブジェクトで直接設定できる `authenticationType`フィールド) で認可設定を直接設定でき、スキーマのデフォルトとして機能します。つまり、特定のディレクティブのないタイプは API レベルの承認設定を渡す必要があります。

スキーマレベルでは、スキーマでディレクティブを使用して追加の承認モードを指定できます。スキーマの個々のフィールドに承認モードを指定できます。たとえば、`API_KEY` 承認の場合、スキーマオブジェクトタイプの定義/フィールドで `@aws_api_key` を使用します。以下のディレクティブは、スキーマフィールドおよびオブジェクトタイプ定義でサポートされています。
+  `@aws_api_key` - フィールドが `API_KEY` で承認されることを指定します。
+  `@aws_iam` - フィールドが `AWS_IAM` で承認されることを指定します。
+  `@aws_oidc` - フィールドが `OPENID_CONNECT` で承認されることを指定します。
+  `@aws_cognito_user_pools` - フィールドが `AMAZON_COGNITO_USER_POOLS` で承認されることを指定します。
+  `@aws_lambda` - フィールドが `AWS_LAMBDA` で承認されることを指定します。

`@aws_auth` ディレクティブを追加の承認モードと共に使用することはできません。`@aws_auth` は、追加の承認モードのない `AMAZON_COGNITO_USER_POOLS` 承認のコンテキストでのみ機能します。ただし、同じ引数で `@aws_auth` ディレクティブの代わりに `@aws_cognito_user_pools` ディレクティブを使用できます。2 つの主な違いは、フィールドとオブジェクトタイプの定義で `@aws_cognito_user_pools` を指定できることです。

追加の承認モードがどのように機能し、スキーマでどのように指定できるかを理解するために、以下のスキーマを見てみましょう。

```
schema {
   query: Query
   mutation: Mutation
}

type Query {
   getPost(id: ID): Post
   getAllPosts(): [Post]
   @aws_api_key
}

type Mutation {
   addPost(
      id: ID!
      author: String!
      title: String!
      content: String!
      url: String!
   ): Post!
}

type Post @aws_api_key @aws_iam {
   id: ID!
   author: String
   title: String
   content: String
   url: String
   ups: Int!
   downs: Int!
   version: Int!
}
...
```

このスキーマでは、 `AWS_IAM`が AWS AppSync GraphQL API のデフォルトの認可タイプであると仮定します。つまり、ディレクティブのないフィールドは `AWS_IAM` を使用して保護されます。たとえば、`Query` タイプの `getPost` フィールドの場合です。スキーマディレクティブにより、複数の承認モードを使用できます。たとえば、 AWS AppSync GraphQL API で を追加の認可モードとして`API_KEY`設定し、 `@aws_api_key`ディレクティブ (`getAllPosts`この例では など) を使用してフィールドをマークできます。ディレクティブはフィールドレベルで機能するため、`Post` タイプへの `API_KEY` アクセスも許可する必要があります。そのためには、`Post` タイプの各フィールドをディレクティブでマークするか、`Post` タイプを `@aws_api_key` ディレクティブでマークします。

`Post` タイプのフィールドへのアクセスをさらに制限するには、以下に示すように、`Post` タイプの個々のフィールドに対してディレクティブを使用できます。

たとえば、`restrictedContent` フィールドを `Post` タイプに追加し、`@aws_iam` ディレクティブを使用してそのフィールドへのアクセスを制限できます。ただし `restrictedContent` に、`AWS_IAM` 認証済みリクエストからはアクセスできますが、`API_KEY` リクエストからはアクセスできません。

```
type Post @aws_api_key @aws_iam{
   id: ID!
   author: String
   title: String
   content: String
   url: String
   ups: Int!
   downs: Int!
   version: Int!
   restrictedContent: String!
   @aws_iam
}
...
```

## きめ細かなアクセスコントロール
<a name="fine-grained-access-control"></a>

前述の情報は、特定の GraphQL フィールドへのアクセスを制限または許可する方法を示しています。特定の条件に基づいて (たとえば、呼び出し元のユーザーがだれであるかや、そのユーザーがデータを所有しているかどうかに基づいて) データに対するアクセスコントロールを設定する場合は、リゾルバーでマッピングテンプレートを使用できます。より複雑なビジネスロジックも実行できます。「[フィルタ処理情報](#aws-appsync-filtering-information)」で説明します。

このセクションでは、DynamoDB リゾルバーマッピングテンプレート使用してデータのアクセスコントロールを設定する方法を示します。

さらに進む前に、 AWS AppSync のマッピングテンプレートがよくわからない場合は、「[リゾルバーのマッピングテンプレートリファレンス](resolver-mapping-template-reference.md#aws-appsync-resolver-mapping-template-reference)」と「[DynamoDB のリゾルバーのマッピングテンプレートリファレンス](resolver-mapping-template-reference-dynamodb.md#aws-appsync-resolver-mapping-template-reference-dynamodb)」を参照してください。

DynamoDB を使用した次の例では、前述のブログ投稿スキーマを使用し、投稿を作成したユーザーのみが編集を許可されているものとします。評価プロセスは、Amazon Cognito ユーザープールなどを使用して、ユーザーがアプリケーションで認証情報を取得し、GraphQL オペレーションの一部として、これらの認証情報を渡すというものです。その後、マッピングテンプレートが、条件ステートメントで、認証情報 (username など) からの値を置き換えます。値はデータベースの値と比較されます。

![\[Diagram showing authentication flow from user login to database operation using AWS のサービス.\]](http://docs.aws.amazon.com/ja_jp/appsync/latest/devguide/images/FGAC.png)


この機能を追加するには、次のように `editPost` GraphQL フィールドを追加します。

```
schema {
   query: Query
   mutation: Mutation
}

type Query {
   posts:[Post!]!
}

type Mutation {
   editPost(id:ID!, title:String, content:String):Post
   addPost(id:ID!, title:String!):Post!
}
...
```

`editPost` のリゾルバーマッピングテンプレート (このセクションの最後にある例) では、投稿を作成したユーザーのみが編集を許可されるように、データストアに対する論理チェックを実行する必要があります。これは編集オペレーションなので、DynamoDB の `UpdateItem` に対応します。ユーザー ID 検証に渡されるコンテキストを使用して、このアクションを実行する前に条件チェックを実行できます。これは次の値を持つ `Identity` オブジェクトに保存されます。

```
{
   "accountId" : "12321434323",
   "cognitoIdentityPoolId" : "",
   "cognitoIdentityId" : "",
   "sourceIP" : "",
   "caller" : "ThisistheprincipalARN",
   "username" : "username",
   "userArn" : "Sameasabove"
}
```

DynamoDB `UpdateItem` コールでこのオブジェクトを使用するには、比較用テーブルにユーザー ID 情報を保存する必要があります。まず、`addPost` ミューテーションは作成者を保存する必要があります。次に、更新する前に、`editPost` ミューテーションで条件チェックを実行する必要があります。

`Author` 列としてユーザー ID を保存する `addPost` のリゾルバーコードの例を次に示します。

```
import { util, Context } from '@aws-appsync/utils';
import { put } from '@aws-appsync/utils/dynamodb';

export function request(ctx) {
	const { id: postId, ...item } = ctx.args;
	return put({
		key: { postId },
		item: { ...item, Author: ctx.identity.username },
		condition: { postId: { attributeExists: false } },
	});
}

export const response = (ctx) => ctx.result;
```

`Author` 属性が、アプリケーションから得られた `Identity` オブジェクトから入力されていることに注意してください。

最後に、`editPost` のリゾルバーコードの例を次に示します。これは、投稿を作成したユーザーからリクエストが来た場合にのみ、ブログ投稿のコンテンツを更新します。

```
import { util, Context } from '@aws-appsync/utils';
import { put } from '@aws-appsync/utils/dynamodb';

export function request(ctx) {
	const { id, ...item } = ctx.args;
	return put({
		key: { id },
		item,
		condition: { author: { contains: ctx.identity.username } },
	});
}

export const response = (ctx) => ctx.result;
```

この例では、`UpdateItem` ではなく、すべての値を上書きする `PutItem` を使用していますが、`condition` ステートメントブロックには同じ概念が適用されます。

## フィルタ処理情報
<a name="aws-appsync-filtering-information"></a>

データソースからのレスポンスを制御できないときに、データソースへの正常な書き込みまたは読み取りに対して、不必要な情報をクライアントに送信したくない場合があります。このような場合は、レスポンスマッピングテンプレートを使用して情報をフィルタリングすることができます。

たとえば、ブログ投稿 DynamoDB テーブルに適切なインデックス (`Author` のインデックスなど) がない場合を考えます。以下のリゾルバーを使用できます。

```
import { util, Context } from '@aws-appsync/utils';
import { get } from '@aws-appsync/utils/dynamodb';

export function request(ctx) {
	return get({ key: { ctx.args.id } });
}

export function response(ctx) {
	if (ctx.result.author === ctx.identity.username) {
		return ctx.result;
	}
	return null;
}
```

リクエストハンドラは、呼び出し元が投稿の作成者でなくても、項目をフェッチします。これによってすべてのデータが返されるのを防ぐために、レスポンスハンドラは呼び出し元が項目の作成者と一致することを確認します。発信者がこのチェックに一致しない場合に、null レスポンスのみが返されます。

## データソースへのアクセス
<a name="data-source-access"></a>

AWS AppSync は、Identity and Access Management ([IAM](https://aws.amazon.com/iam/)) ロールとアクセスポリシーを使用してデータソースと通信します。既存のロールを使用している場合、 AWS AppSync がロールを引き受けるには、信頼ポリシーを追加する必要があります。信頼関係は以下のようになります。

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "appsync.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
```

------

必要な最小限のリソースについて作業を行うアクセス許可のみを持つように、ロールのアクセスポリシーを制限することが重要です。AppSync コンソールを使用してデータソースを作成し、ロールを作成したとき、これは自動的に行われます。ただし、IAM コンソールから組み込みサンプルテンプレートを使用して AWS AppSync コンソールの外部でロールを作成する場合、アクセス許可はリソースに対して自動的にスコープダウンされないため、アプリケーションを本番環境に移行する前にこのアクションを実行する必要があります。

# リクエストとレスポンスを保護するためのアクセスコントロールのユースケース
<a name="security-authorization-use-cases"></a>

「[セキュリティ](security-authz.md#aws-appsync-security)」セクションでは、API を保護するためのさまざまな認可モードについて説明し、きめ細かな認可メカニズムの概念と流れについて紹介します。 AWS AppSync では、GraphQL Resolver [Mapping テンプレート](resolver-mapping-template-reference-overview.md#aws-appsync-resolver-mapping-template-reference-overview)を使用してデータに対してロジックフルオペレーションを実行できるため、ユーザー ID、条件、データインジェクションの組み合わせを使用して、読み取りまたは書き込み時のデータを非常に柔軟に保護できます。

 AWS AppSync Resolver の編集に慣れていない場合は、[プログラミングガイド](resolver-mapping-template-reference-programming-guide.md#aws-appsync-resolver-mapping-template-reference-programming-guide)を参照してください。

## 概要:
<a name="overview"></a>

システム内のデータへのアクセスの付与は、従来、行 (リソース) と列 (ユーザーまたはロール) の交点が付与されるアクセス許可である [アクセスコントロールマトリックス](https://en.wikipedia.org/wiki/Access_Control_Matrix) を通じて行われています。

AWS AppSync は独自のアカウントのリソースを使用し、アイデンティティ (ユーザー/ロール) 情報を[コンテキストオブジェクト](resolver-context-reference.md#aws-appsync-resolver-mapping-template-context-reference)として GraphQL リクエストとレスポンスにスレッドします。これはリゾルバーで使用できます。つまり、リゾルバーのロジックに基づいて、書き込みまたは読み取りのオペレーションに対して適切にアクセス権限を付与できます。このロジックがリソースレベルである場合、たとえば、特定の名前付きユーザーまたはグループのみが特定のデータベース行に読み書きできる場合、その「認可メタデータ」を保存する必要があります。 AWS AppSync はデータを保存しないため、アクセス許可を計算できるように、この認可メタデータをリソースに保存する必要があります。認可メタデータは通常、DynamoDB テーブル内の属性 (列) であり、**所有者**、ユーザーまたはグループのリストなどです。たとえば、**Readers** と **Writers** 属性があります。

ハイレベルでは、データソースから個々の項目を読み取っている場合、リゾルバーがデータソースから読み取った後に、レスポンステンプレートで条件ステートメント `#if () ... #end` を実行します。そのチェックでは通常、読み取りオペレーションから返された認可メタデータに対するメンバーシップ確認のために、`$context.identity` の user または group の値が使用されます。複数のレコードがある (テーブルの `Scan` や `Query` で返されるリストなど) 場合は、同様の user または group の値を使用して、データソースに対するオペレーションの一部として条件チェックを送信します。

同様に、データを書き込む場合は、アクション (ミューテーションを作成するユーザーやグループにアクセス権限があるかどうかを確認する `PutItem` や `UpdateItem` など) に対して条件ステートメントを適用します。この条件チェックでは、`$context.identity` 内の値を使用して何度も行われ、リソースの認可メタデータと比較されます。リクエストテンプレートとレスポンステンプレートの両方で、クライアントからのカスタムヘッダーを使用して検証チェックを実行することもできます。

## データの読み込み
<a name="reading-data"></a>

前述のように、チェックを実行するための認可メタデータは、リソースに保存されているかまたは GraphQL リクエスト (ID、ヘッダーなど) で渡される必要があります。その例を示すために、次の DynamoDB テーブルがあるとします。

![\[DynamoDB table with ID, Data, PeopleCanAccess, GroupsCanAccess, and Owner columns.\]](http://docs.aws.amazon.com/ja_jp/appsync/latest/devguide/images/auth.png)


プライマリキーは `id` であり、アクセスするデータは `Data` です。その他の列は、認可のために実行できるチェックの例です。「[DynamoDB のリゾルバーのマッピングテンプレートリファレンス](resolver-mapping-template-reference-dynamodb.md#aws-appsync-resolver-mapping-template-reference-dynamodb)」に概説されている通り、`Owner` は `String` になり、`PeopleCanAccess` と `GroupsCanAccess` は `String Sets` になります。

「[リゾルバーのマッピングテンプレートの概要](resolver-mapping-template-reference-overview.md#aws-appsync-resolver-mapping-template-reference-overview)」の図に示しているように、レスポンステンプレートには、コンテキストオブジェクトだけでなくデータソースからの結果も含まれています。個々の項目に対する GraphQL クエリでは、レスポンステンプレートを使用して、そのユーザーが結果を確認することを許可されているかどうかをチェックし、そうでない場合は認可エラーメッセージを返すことができます。これは、「認可フィルタ」と呼ばれることもあります。Scan または Query を使用してリストを返す GraphQL クエリでは、リクエストテンプレートでチェックを実行し、認可条件が満たされている場合にのみデータを返すのが、より効率的です。次のように実装します。

1. GetItem - 個々のレコードに対する認可チェック。`#if() ... #end` ステートメントを使用して行われます。

1. Scan/Query オペレーション - 認可チェックは `"filter":{"expression":...}` ステートメントです。よく使用されるチェックは、等価チェック (`attribute = :input`)、または値がリストあるかどうかのチェック (`contains(attribute, :input)`) です。

上記の 2 で両方のステートメントにある `attribute` は、テーブル内のレコードの列名 (上記の例では `Owner` など) を表しています。そのエイリアスとして `#` 記号と `"expressionNames":{...}` を使用できますが、必須ではありません。`:input` は、データベース属性と比較する値への参照であり、`"expressionValues":{...}` で定義します。その例を以下に示します。

### ユースケース: 所有者が読み取り可能
<a name="use-case-owner-can-read"></a>

上記のテーブルを使用して、個々の読み取りオペレーション (`Owner == Nadia`) で `GetItem` であるときにのみデータを返す場合、テンプレートは次のようになります。

```
#if($context.result["Owner"] == $context.identity.username)
    $utils.toJson($context.result)
#else
    $utils.unauthorized()
#end
```

ここで、以降のセクションで再利用する点について説明しておきます。まず、チェックで使用される `$context.identity.username` は、Amazon Cognito ユーザープールが使用されている場合は分かりやすいユーザーサインアップ名であり、IAM (Amazon Cognito フェデレーテッドアイデンティティも含む) が使用されている場合はユーザー ID です。所有者に対して保存されるその他の値として、複数のロケーションからフェデレーテッドログインする場合に便利な一意の「Amazon Cognito ID」値などがあり、「[リゾルバーのマッピングテンプレートのコンテキストリファレンス](resolver-context-reference.md#aws-appsync-resolver-mapping-template-context-reference)」で、利用可能なオプションを確認しておく必要があります。

次に、`$util.unauthorized()` に対応する条件付き else チェックは、完全に省略可能ですが、GraphQL API を設計する際のベストプラクティスとして、指定することお勧めします。

### ユースケース: 特定のアクセス権のハードコード
<a name="use-case-hardcode-specific-access"></a>

```
// This checks if the user is part of the Admin group and makes the call
#foreach($group in $context.identity.claims.get("cognito:groups"))
    #if($group == "Admin")
        #set($inCognitoGroup = true)
    #end
#end
#if($inCognitoGroup)
{
    "version" : "2017-02-28",
    "operation" : "UpdateItem",
    "key" : {
        "id" : $util.dynamodb.toDynamoDBJson($ctx.args.id)
    },
    "attributeValues" : {
        "owner" : $util.dynamodb.toDynamoDBJson($context.identity.username)
        #foreach( $entry in $context.arguments.entrySet() )
            ,"${entry.key}" : $util.dynamodb.toDynamoDBJson($entry.value)
        #end
    }
}
#else
    $utils.unauthorized()
#end
```

### ユースケース: 結果リストのフィルタリング
<a name="use-case-filtering-a-list-of-results"></a>

前の例では単一の項目が返されるため、`$context.result` に対してチェックを直接実行することもできましたが、スキャンなどの一部のオペレーションでは `$context.result.items` で複数の項目が返されるため、認可フィルタを実行して、そのユーザーが確認を許可されている結果のみを返す必要があります。たとえば、今度はレコードに設定されている Amazon Cognito IdentityID が `Owner` フィールドにあるとすると、次のレスポンスマッピングテンプレートを使用して、そのユーザーが所有しているレコードのみを示すようにフィルタリングできます。

```
#set($myResults = [])
#foreach($item in $context.result.items)
    ##For userpools use $context.identity.username instead
    #if($item.Owner == $context.identity.cognitoIdentityId)
        #set($added = $myResults.add($item))
    #end
#end
$utils.toJson($myResults)
```

### ユースケース: 複数のユーザーが読み取り可能
<a name="use-case-multiple-people-can-read"></a>

もう 1 つのよくある認可オプションは、ユーザーのグループがデータを読み取ることができるように許可することです。次の例の `"filter":{"expression":...}` では、GraphQL クエリを実行しているユーザーが `PeopleCanAccess` のセットにリストされている場合にのみ、テーブルスキャンからの値が返されます。

```
{
    "version" : "2017-02-28",
    "operation" : "Scan",
    "limit": #if(${context.arguments.count}) $util.toJson($context.arguments.count) #else 20 #end,
    "nextToken": #if(${context.arguments.nextToken})  $util.toJson($context.arguments.nextToken) #else null #end,
    "filter":{
        "expression": "contains(#peopleCanAccess, :value)",
        "expressionNames": {
                "#peopleCanAccess": "peopleCanAccess"
        },
        "expressionValues": {
                ":value": $util.dynamodb.toDynamoDBJson($context.identity.username)
        }
    }
}
```

### ユースケース: グループが読み取り可能
<a name="use-case-group-can-read"></a>

直前のユースケースと同様に、1 つまたは複数のグループに属するユーザーのみが、データベース内の特定の項目を読み取る権限を持っているとします。`"expression": "contains()"` オペレーションを使用するのは同じですが、設定されているメンバーシップに属している必要があるすべてのグループの論理 OR です。この例では、ユーザーが属している各グループに対して次の `$expression` ステートメントを作成し、それをフィルタに渡します。

```
#set($expression = "")
#set($expressionValues = {})
#foreach($group in $context.identity.claims.get("cognito:groups"))
    #set( $expression = "${expression} contains(groupsCanAccess, :var$foreach.count )" )
    #set( $val = {})
    #set( $test = $val.put("S", $group))
    #set( $values = $expressionValues.put(":var$foreach.count", $val))
    #if ( $foreach.hasNext )
    #set( $expression = "${expression} OR" )
    #end
#end
{
    "version" : "2017-02-28",
    "operation" : "Scan",
    "limit": #if(${context.arguments.count}) $util.toJson($context.arguments.count) #else 20 #end,
    "nextToken": #if(${context.arguments.nextToken})  $util.toJson($context.arguments.nextToken) #else null #end,
    "filter":{
        "expression": "$expression",
        "expressionValues": $utils.toJson($expressionValues)
    }
}
```

## データの書き込み
<a name="writing-data"></a>

ミューテーションでのデータの書き込みは、常にリクエストマッピングテンプレートで制御されます。DynamoDB のデータソースの場合、key は、そのテーブル内の認可メタデータに対して検証を実行する適切な `"condition":{"expression"...}"` が使用されます。「[セキュリティ](security-authz.md#aws-appsync-security)」には、テーブル内の `Author` フィールドのチェックに役立つ例があります。このセクションでは、その他のユースケースを示します。

### ユースケース: 複数の所有者
<a name="use-case-multiple-owners"></a>

以前の例のテーブル図を使用した、`PeopleCanAccess` リストがあるとします。

```
{
    "version" : "2017-02-28",
    "operation" : "UpdateItem",
    "key" : {
        "id" : $util.dynamodb.toDynamoDBJson($ctx.args.id)
    },
    "update" : {
        "expression" : "SET meta = :meta",
        "expressionValues": {
            ":meta" : $util.dynamodb.toDynamoDBJson($ctx.args.meta)
        }
    },
    "condition" : {
        "expression"       : "contains(Owner,:expectedOwner)",
        "expressionValues" : {
            ":expectedOwner" : $util.dynamodb.toDynamoDBJson($context.identity.username)
        }
    }
}
```

### ユースケース: グループが新規レコードを作成可能
<a name="use-case-group-can-create-new-record"></a>

```
#set($expression = "")
#set($expressionValues = {})
#foreach($group in $context.identity.claims.get("cognito:groups"))
    #set( $expression = "${expression} contains(groupsCanAccess, :var$foreach.count )" )
    #set( $val = {})
    #set( $test = $val.put("S", $group))
    #set( $values = $expressionValues.put(":var$foreach.count", $val))
    #if ( $foreach.hasNext )
    #set( $expression = "${expression} OR" )
    #end
#end
{
    "version" : "2017-02-28",
    "operation" : "PutItem",
    "key" : {
        ## If your table's hash key is not named 'id', update it here. **
        "id" : $util.dynamodb.toDynamoDBJson($ctx.args.id)
        ## If your table has a sort key, add it as an item here. **
    },
    "attributeValues" : {
        ## Add an item for each field you would like to store to Amazon DynamoDB. **
        "title" : $util.dynamodb.toDynamoDBJson($ctx.args.title),
        "content": $util.dynamodb.toDynamoDBJson($ctx.args.content),
        "owner": $util.dynamodb.toDynamoDBJson($context.identity.username)
    },
    "condition" : {
        "expression": $util.toJson("attribute_not_exists(id) AND $expression"),
        "expressionValues": $utils.toJson($expressionValues)
    }
}
```

### ユースケース: グループが既存レコードを更新可能
<a name="use-case-group-can-update-existing-record"></a>

```
#set($expression = "")
#set($expressionValues = {})
#foreach($group in $context.identity.claims.get("cognito:groups"))
    #set( $expression = "${expression} contains(groupsCanAccess, :var$foreach.count )" )
    #set( $val = {})
    #set( $test = $val.put("S", $group))
    #set( $values = $expressionValues.put(":var$foreach.count", $val))
    #if ( $foreach.hasNext )
    #set( $expression = "${expression} OR" )
    #end
#end
{
    "version" : "2017-02-28",
    "operation" : "UpdateItem",
    "key" : {
        "id" : $util.dynamodb.toDynamoDBJson($ctx.args.id)
    },
    "update":{
                "expression" : "SET title = :title, content = :content",
        "expressionValues": {
            ":title" : $util.dynamodb.toDynamoDBJson($ctx.args.title),
            ":content" : $util.dynamodb.toDynamoDBJson($ctx.args.content)
        }
    },
    "condition" : {
        "expression": $util.toJson($expression),
        "expressionValues": $utils.toJson($expressionValues)
    }
}
```

## パブリックレコードとプライベートレコード
<a name="public-and-private-records"></a>

条件フィルターを使用すると、データをプライベート、パブリック、またはその他のブール型チェックとしてマークすることもできます。それを認可フィルタの一部としてレスポンステンプレート内に組み込むことができます。このチェックを使用すると、グループメンバーシップを制御することなく、データを一時的に隠したり、ビューから除外したりできます。

たとえば、DynamoDB テーブル内の各項目に、`yes` または `no` のいずれかの値を持つ `public` という属性を追加するとします。次のレスポンステンプレートを `GetItem` 呼び出しで使用すると、アクセス権があるグループにユーザーが属していて、かつ、そのデータがパブリックとマークされている場合にのみ、データを表示できます。

```
#set($permissions = $context.result.GroupsCanAccess)
#set($claimPermissions = $context.identity.claims.get("cognito:groups"))

#foreach($per in $permissions)
    #foreach($cgroups in $claimPermissions)
        #if($cgroups == $per)
            #set($hasPermission = true)
        #end
    #end
#end

#if($hasPermission && $context.result.public == 'yes')
    $utils.toJson($context.result)
#else
    $utils.unauthorized()
#end
```

また、上記のコードで論理 OR (`||`) を使用すると、ユーザーにレコードへのアクセス許可があるか、または、レコードがパブリックである場合に、そのユーザーに読み取りを許可できます。

```
#if($hasPermission || $context.result.public == 'yes')
    $utils.toJson($context.result)
#else
    $utils.unauthorized()
#end
```

通常、認可チェックを実行する際に、標準的な演算子 `==`、`!=`、`&&`、および `||` が役立ちます。

## リアルタイムデータ
<a name="security-real-time-data"></a>

クライアントがサブスクリプションを作成したときに、このドキュメントで前述したのと同じ手法で、きめ細かなアクセス制御コントロールを GraphQL のサブスクリプションに適用できます。サブスクリプションフィールドにリゾルバーをアタッチすると、そのポイントで、データソースからデータをクエリし、リクエストまたはレスポンスのいずれかのマッピングテンプレートで条件ロジックを実行できます。そのデータ構造が、GraphQL サブスクリプションで返される型と一致している限り、追加のデータ (サブスクリプションからの初期結果など) をクライアントに返すこともできます。

### ユースケース: ユーザーが特定の対話のみのサブスクライブ可能
<a name="use-case-user-can-subscribe-to-specific-conversations-only"></a>

GraphQL サブスクリプションを使用したリアルタイムデータのよくあるユースケースは、メッセージングやプライベートチャットのアプリケーションを構築することです。複数のユーザーに対応したチャットアプリケーションを作成する場合、2 人または複数ユーザーの間で対話が行われます。ユーザーは、プライベートまたはパブリックの「ルーム」にグループ化されます。したがって、ユーザーにアクセス権が付与されている対話 (1 対 1 またはグループ間の) をサブスクライブする 1 人のユーザーのみを認可します。デモの目的で、以下の単純な例では、1 人のユーザーが別のユーザーにプライベートのメッセージを送信するユースケースを示します。次の 2 つの Amazon DynamoDB テーブルをセットアップします。
+ Messages テーブル: (プライマリキー) `toUser`、(ソートキー) `id` 
+ Permissions テーブル: (プライマリキー) `username` 

Messages テーブルには、GraphQL ミューテーション経由で送信される実際のメッセージが保存されます。Permissions テーブルは、クライアントの接続時に認可のために GraphQL サブスクリプションによってチェックされます。以下の例では、次の GraphQL スキーマを使用していることを前提としています。

```
input CreateUserPermissionsInput {
    user: String!
    isAuthorizedForSubscriptions: Boolean
}

type Message {
    id: ID
    toUser: String
    fromUser: String
    content: String
}

type MessageConnection {
    items: [Message]
    nextToken: String
}

type Mutation {
    sendMessage(toUser: String!, content: String!): Message
    createUserPermissions(input: CreateUserPermissionsInput!): UserPermissions
    updateUserPermissions(input: UpdateUserPermissionInput!): UserPermissions
}

type Query {
    getMyMessages(first: Int, after: String): MessageConnection
    getUserPermissions(user: String!): UserPermissions
}

type Subscription {
    newMessage(toUser: String!): Message
        @aws_subscribe(mutations: ["sendMessage"])
}

input UpdateUserPermissionInput {
    user: String!
    isAuthorizedForSubscriptions: Boolean
}

type UserPermissions {
    user: String
    isAuthorizedForSubscriptions: Boolean
}

schema {
    query: Query
    mutation: Mutation
    subscription: Subscription
}
```

以下では、サブスクリプションリゾルバーを示すために、一部の標準オペレーション (`createUserPermissions()` など) は取り上げていませんが、DynamoDB リゾルバーで標準実装されています。代わりに、リゾルバーでのサブスクリプションの認可フローを中心に説明します。ユーザー間でメッセージを送信するには、`sendMessage()` フィールドにリゾルバーをアタッチし、次のリクエストテンプレートを使用して **Messages** テーブルデータソースを選択します。

```
{
    "version" : "2017-02-28",
    "operation" : "PutItem",
    "key" : {
        "toUser" : $util.dynamodb.toDynamoDBJson($ctx.args.toUser),
        "id" : $util.dynamodb.toDynamoDBJson($util.autoId())
    },
    "attributeValues" : {
        "fromUser" : $util.dynamodb.toDynamoDBJson($context.identity.username),
        "content" : $util.dynamodb.toDynamoDBJson($ctx.args.content),
    }
}
```

この例では、`$context.identity.username` を使用します。これにより、 AWS Identity and Access Management または Amazon Cognito ユーザーのユーザー情報が返されます。レスポンステンプレートは、`$util.toJson($ctx.result)` の単純なパススルーです。保存してスキーマページに戻ります。次に、`newMessage()`Permissions** テーブルをデータソースとして使用し、次のリクエストマッピングテンプレートを使用して、** サブスクリプションにリゾルバーをアタッチします。

```
{
    "version": "2018-05-29",
    "operation": "GetItem",
    "key": {
        "username": $util.dynamodb.toDynamoDBJson($ctx.identity.username),
    },
}
```

次のレスポンスマッピングテンプレートを使用し、**Permissions** テーブルからのデータを使用して認可チェックを実行します。

```
#if(! ${context.result})
    $utils.unauthorized()
#elseif(${context.identity.username} != ${context.arguments.toUser})
    $utils.unauthorized()
#elseif(! ${context.result.isAuthorizedForSubscriptions})
    $utils.unauthorized()
#else
##User is authorized, but we return null to continue
    null
#end
```

この例の場合、3 つの認可チェックを実行しています。1 つ目は、結果が返されることを確認します。2 つ目は、そのユーザーが別のユーザーに対するメッセージをサブスクライブしていないことを確認しています。3 番目のチェックでは、`isAuthorizedForSubscriptions` として保存されている `BOOL` の DynamoDB 属性をチェックすることで、そのユーザーが任意のフィールドへのサブスクライブを許可されていることを確認しています。

テストするには、Amazon Cognito ユーザープールと「Nadia」という名前のユーザーを使用して AWS AppSync コンソールにサインインし、次の GraphQL サブスクリプションを実行します。

```
subscription AuthorizedSubscription {
    newMessage(toUser: "Nadia") {
        id
        toUser
        fromUser
        content
    }
}
```

**Permissions** テーブルに、`Nadia` の `username` キー属性に対して `isAuthorizedForSubscriptions` が `true` に設定されているレコードがある場合は、正常なレスポンスが表示されます。上記の `username` クエリで別の `newMessage()` を試行すると、エラーが返されます。

# AWS WAF を使用して AWS AppSync APIsを保護する
<a name="WAF-Integration"></a>

AWS WAF は、ウェブアプリケーションと APIsから保護するのに役立つウェブアプリケーションファイアウォールです。お客様が定義するカスタマイズ可能なウェブセキュリティルールと条件に基づいて、ウェブリクエストを許可、ブロック、またはモニタリング (カウント) する一連のルール (ウェブアクセスコントロールリストまたはウェブ ACL と呼ばれます) を設定することができます。 AWS AppSync API を と統合すると AWS WAF、API が受け入れる HTTP トラフィックをより詳細に制御し、可視化できます。詳細については AWS WAF、「 AWS WAF デベロッパーガイド」の[AWS WAF 「 の仕組み](https://docs.aws.amazon.com/waf/latest/developerguide/how-aws-waf-works.html)」を参照してください。

を使用して AWS WAF 、SQL インジェクションやクロスサイトスクリプティング (XSS) 攻撃などの一般的なウェブエクスプロイトから AppSync API を保護できます。これらは、API の可用性とパフォーマンスに影響を与え、セキュリティを侵害したり、過剰なリソースを消費したりする可能性があります。例えば、指定した IP アドレス範囲からのリクエスト、CIDR ブロックからのリクエスト、特定の国またはリージョンからのリクエスト、悪意のある SQL コードを含むリクエスト、悪意のあるスクリプトを含むリクエストを許可またはブロックするルールを作成できます。

また、HTTP ヘッダー、メソッド、クエリ文字列、URI、およびリクエストボディの指定された文字列または定型表現パターン (最初の 8 KB に制限されます) に一致するルールを作成することもできます。さらに、特定のユーザーエージェント、悪質なボット、またはコンテンツスクレーパーからの攻撃をブロックするルールを作成できます。例えば、レートベースのルールを使用して、継続的に更新される後続の 5 分間で、各クライアント IP によって許可されるウェブリクエストの数を指定できます。

サポートされるルールのタイプと追加 AWS WAF 機能の詳細については、 [AWS WAF デベロッパーガイド](https://docs.aws.amazon.com/waf/latest/developerguide/waf-chapter.html)および [AWS WAF API リファレンス](https://docs.aws.amazon.com/waf/latest/APIReference/API_Types_AWS_WAFV2.html)を参照してください。

**重要**  
AWS WAF は、ウェブエクスプロイトに対する防御の最前線です。API で AWS WAF が有効になっている場合は、API キー認可、IAM ポリシー、OIDC トークン、および Amazon Cognito ユーザープールなど他のアクセスコントロール機能の前に AWS WAF ルールが評価されます。

## AppSync API を と統合する AWS WAF
<a name="integrate-API-with-WAF"></a>

Appsync API は、 AWS マネジメントコンソール、 AWS CLI、またはその他の互換性のあるクライアント AWS WAF を使用して AWS CloudFormationと統合できます。

**AWS AppSync API を と統合するには AWS WAF**

1.  AWS WAF ウェブ ACL を作成します。[AWS WAF コンソール](https://console.aws.amazon.com/waf/)を使用する詳細なステップについては、「[ウェブ ACL の作成](https://docs.aws.amazon.com/waf/latest/developerguide/web-acl-creating.html)」を参照してください。

1. ウェブ ACL のルールを定義します。1 つまたは複数のルールが、ウェブ ACL を作成するプロセスで定義されます。ルールを構成する方法の詳細については、[AWS WAF ルール](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rules.html)を参照してください。 AWS AppSync API に定義できる便利なルールの例については、「」を参照してください[ウェブ ACL のルールの作成](#Creating-web-acl-rules)。

1. ウェブ ACL を AWS AppSync API に関連付けます。このステップは、[AWS WAF コンソール](https://console.aws.amazon.com/wafv2/)または、[AppSync コンソール](https://console.aws.amazon.com/appsync/)で実行できます。
   + ウェブ ACL を AWS WAF コンソールの AWS AppSync API に関連付けるには、 AWS WAF デベロッパーガイドの[「 リソースとの AWS ウェブ ACL の関連付けまたは関連付け解除](https://docs.aws.amazon.com/waf/latest/developerguide/web-acl-associating-aws-resource.html)」の手順に従います。
   + コンソールでウェブ ACL を AWS AppSync API AWS AppSync に関連付けるには

     1. にサインイン AWS マネジメントコンソール し、[AppSync コンソール](https://console.aws.amazon.com/appsync/)を開きます。

     1. ウェブ ACL に関連付ける API を選択します。

     1. ナビゲーションペインで **[設定]** を選択します。

     1. **Web アプリケーションファイアウォール**セクションで、**有効化 AWS WAF**をオンにします。

     1. **ウェブ ACL**ドロップダウンリストで、API に関連付けるウェブ ACL の名前を選択します。

     1. **保存**を選択してウェブ ACL を API に関連付けます。

   

**注記**  
 AWS WAF コンソールでウェブ ACL を作成した後、新しいウェブ ACL が使用可能になるまでに数分かかることがあります。**Web アプリケーションファイアウォール**メニューで新しく作成されたウェブ ACL が表示されない場合、数分待ってから、ウェブ ACL を API に関連付ける手順を再試行します。

**注記**  
AWS WAF 統合は、リアルタイムエンドポイントの `Subscription registration message` イベントのみをサポートします。 AWS AppSync は、`Subscription registration message`ブロックされた の`start_ack`メッセージではなく、エラーメッセージで応答します AWS WAF。

ウェブ ACL を AWS AppSync API に関連付けると、 AWS WAF APIs を使用してウェブ ACL を管理します。 AWS AppSync API を別のウェブ ACL に関連付ける場合を除き、ウェブ ACL を AWS AppSync API に再関連付けする必要はありません。

## ウェブ ACL のルールの作成
<a name="Creating-web-acl-rules"></a>

ルールは、ウェブリクエストの検査方法と、ウェブリクエストが検査基準に一致した場合の処理を定義します。ルールは AWS WAF 、それ自体で には存在しません。ルールが定義されているルールグループまたはウェブ ACL に含まれる名前を使用してルールにアクセスします。詳細については、「[AWS WAF ルール](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rules.html)」を参照してください。次の例は、AppSync API の保護に役立つルールを定義し、関連付ける方法を示しています。

**Example リクエストボディのサイズを制限するウェブ ACL ルール**  
次に、リクエストのボディサイズを制限するルールの例を示します。これは、 AWS WAF コンソールでウェブ ACL を作成するときに **Rule JSON エディタ**に入力されます。  

```
{
    "Name": "BodySizeRule", 
    "Priority": 1, 
    "Action": {
        "Block": {}
    }, 
    "Statement": {
        "SizeConstraintStatement": {
            "ComparisonOperator": "GE",
            "FieldToMatch": {
                "Body": {}
            },
            "Size": 1024, 
            "TextTransformations": [
                {
                    "Priority": 0, 
                    "Type": "NONE"
                }
             ]
          }
       }, 
       "VisibilityConfig": {
           "CloudWatchMetricsEnabled": true, 
           "MetricName": "BodySizeRule", 
           "SampledRequestsEnabled": true
        }
}
```
前述のルール例を使用してウェブ ACL を作成したら、それを AppSync API に関連付ける必要があります。を使用する代わりに AWS マネジメントコンソール、次のコマンド AWS CLI を実行して、 でこのステップを実行できます。  

```
aws waf associate-web-acl --web-acl-id waf-web-acl-arn --resource-arn appsync-api-arn
```
変更が伝播されるまで数分かかる場合がありますが、このコマンドを実行した後、1024 バイトを超える本文を含む要求は、 AWS AppSyncによって拒否されます。  
 AWS WAF コンソールで新しいウェブ ACL を作成した後、ウェブ ACL が API に関連付けるまでに数分かかることがあります。CLI コマンドを実行して、`WAFUnavailableEntityException` エラーを取得し、数分待ってからコマンドを再実行してください。

**Example 単一の IP アドレスからの要求を制限するウェブ ACL ルール**  
以下は、5 分間の単一の IP アドレスからの 100 リクエストに AppSync API をスロットリングするルールの例です。これは、 AWS WAF コンソールでレートベースのルールを使用してウェブ ACL を作成するときに、Rule **JSON エディタ**に入力されます。  

```
{
  "Name": "Throttle",
  "Priority": 0,
  "Action": {
    "Block": {}
  },
  "VisibilityConfig": {
    "SampledRequestsEnabled": true,
    "CloudWatchMetricsEnabled": true,
    "MetricName": "Throttle"
  },
  "Statement": {
    "RateBasedStatement": {
      "Limit": 100,
      "AggregateKeyType": "IP"
    }
  }
}
```
前述のルール例を使用してウェブ ACL を作成したら、それを AppSync API に関連付ける必要があります。このステップは、次のコマンド AWS CLI を実行して で実行できます。  

```
aws waf associate-web-acl --web-acl-id waf-web-acl-arn --resource-arn appsync-api-arn
```

**Example API に対するすべての GraphQL \$1\$1schema イントロスペクションクエリを防止するためのウェブ ACL ルール**  
次に、API に対するすべての G GraphQL \$1\$1schema イントロスペクションクエリを禁止するルールの例を示します。文字列「\$1\$1schema」を含む HTTP ボディはすべてブロックされます。これは、 AWS WAF コンソールでウェブ ACL を作成するときに、**ルール JSON エディタ**に入力されます。  

```
{
  "Name": "BodyRule",
  "Priority": 5,
  "Action": {
    "Block": {}
  },
  "VisibilityConfig": {
    "SampledRequestsEnabled": true,
    "CloudWatchMetricsEnabled": true,
    "MetricName": "BodyRule"
  },
  "Statement": {
    "ByteMatchStatement": {
      "FieldToMatch": {
        "Body": {}
      },
      "PositionalConstraint": "CONTAINS",
      "SearchString": "__schema",
      "TextTransformations": [
        {
          "Type": "NONE",
          "Priority": 0
        }
      ]
    }
  }
}
```
前述のルール例を使用してウェブ ACL を作成したら、それを AppSync API に関連付ける必要があります。このステップは、次のコマンド AWS CLI を実行して で実行できます。  

```
aws waf associate-web-acl --web-acl-id waf-web-acl-arn --resource-arn appsync-api-arn
```