AWS Amplify の AppSync API に IAM 認証を追加する

## やりたいこと

![appsync-iam.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27764/8dad13e5-486b-cbf3-951f-70b9a73836a8.jpeg)

[AWS Amplify](https://aws-amplify.github.io/docs/) を使ってアプリ開発を行っていて、後でバッチ処理などで Lambda や EC2 からも Mutation を発行したいという想定です。
直接 DynamoDB をいじってしまうのもありですが、 AppSync で定義したスキーマから外れたデータを投入できてしまったり、 Subscription が発行されなかったりするので AppSync 経由でデータを更新します。

Lambda 関数の詳しい実装については以下の記事が参考になるので割愛します。

[AWS Lambda から AppSync の API を ぶん殴る](https://blog.r3it.com/aws-lambda-to-appsync-1aa0c2f1da04)
[LambdaからAppSyncをIAM認証で使う](https://qiita.com/nakayama_cw/items/df0a1952c7fb2d9b80f8)

## やり方

[複数認証タイプのサポート](https://aws.amazon.com/jp/about-aws/whats-new/2019/05/aws-appsync-now-supports-configuring-multiple-authorization-type/)によって、ユーザープール認証と IAM 認証を同時に使うことができるようになりました。

AppSync のスキーマ上では `@aws_cognito_user_pools` や `@aws_iam` のアノテーションを付与することで認証方法を制御可能です。 [(公式ドキュメント)](https://docs.aws.amazon.com/ja_jp/appsync/latest/devguide/security.html#using-additional-authorization-modes)

しかし、同じように Amplify のスキーマを書いてみてもエラーになりました。 Apmlify では [`@auth` ディレクティブ](https://aws-amplify.github.io/docs/cli-toolchain/graphql#auth)を使います。

```graphql
# 通常の model
type Todo @model {
  id: ID!
  name: String!
  description: String
}

# IAM 認証からもアクセス可能な model
type Todo
  @model
  @auth(rules: [{ allow: private }, { allow: private, provider: iam }]) {
  id: ID!
  name: String!
  description: String
}
```

今までユーザープール認証を使っていて、初めて IAM 認証を追加した後に `amplify push` をしようとすると、 `iam` プロバイダーがないというエラーにより失敗します。

```
@auth directive with 'iam' provider found, but the project has no IAM authentication provider configured.
```

この場合は `amplify update api` コマンドによって IAM 認証を追加します。

```
$ amplify update api
? Please select from one of the below mentioned services
> GraphQL

? Choose the default authorization type for the API
> Amazon Cognito User Pool

? Do you want to configure advanced settings for the GraphQL API
> Yes, I want to make some additional changes.

? Choose the additional authorization types you want to configure for the API
[x] IAM

GraphQL schema compiled successfully.
```

## 生成されたコード

`Todo` や関連する Query, Mutation, Subscription に `@aws_iam` `@aws_cognito_user_pools` が付与されていることがわかります。通常の AppSync で設定する場合と同じようになっていますね。

```graphql
type Todo @aws_iam @aws_cognito_user_pools {
  id: ID!
  name: String!
  description: String
}

type ModelTodoConnection @aws_iam @aws_cognito_user_pools {
  items: [Todo]
  nextToken: String
}

type Query {
  getTodo(id: ID!): Todo @aws_iam @aws_cognito_user_pools
  listTodos(filter: ModelTodoFilterInput, limit: Int, nextToken: String): ModelTodoConnection @aws_iam @aws_cognito_user_pools
}

type Mutation {
  createTodo(input: CreateTodoInput!): Todo @aws_iam @aws_cognito_user_pools
  updateTodo(input: UpdateTodoInput!): Todo @aws_iam @aws_cognito_user_pools
  deleteTodo(input: DeleteTodoInput!): Todo @aws_iam @aws_cognito_user_pools
}

type Subscription {
  onCreateTodo: Todo @aws_subscribe(mutations: ["createTodo"]) @aws_iam @aws_cognito_user_pools
  onUpdateTodo: Todo @aws_subscribe(mutations: ["updateTodo"]) @aws_iam @aws_cognito_user_pools
  onDeleteTodo: Todo @aws_subscribe(mutations: ["deleteTodo"]) @aws_iam @aws_cognito_user_pools
}
```

## 所感

1年くらい Amplify を使っていますが結構な頻度で機能追加されていたりドキュメントが変わっていたりするので、まだまだ発展途上だなというように感じます
（今回も別のところでハマってしまって2時間くらいデプロイに失敗しました）

ソースコードや Issue を追ったり、場合によってはプルリクを出すくらいのつもりで付き合っていくのがいいと思います。