Amplify の AppSync で Cognito の所属グループに基づく認証処理を実装する

# 本記事について
Amplify の AppSync で、Cognito の所属グループに基づく認証処理を挟む方法について記載します。
具体的には、**当該の Cognito User が、"Admin"という名前のグループに入っている場合のみ、AppSync の Query や Mutation 操作ができるようにする** 方法について記載をします。

### 補足
Amplify の勉強時のメモを公開するものです。
私自身、勉強途中で理解が浅い部分もあります。
記載している方法は、必ずしも適切な方法ではないかもしれません。その旨ご理解ください。
メモレベルの内容ですが、後続の方のお役に立てれば幸いです。


# 想定ケース
以下のような GraphQL スキーマを仮定します。

```:<project-root>/amplify/backend/api/<api-name>/schema.graphql
### 1
type Mutation{
  functionA(
    user:  String!
  ): String @function(name: "functionA-${env}")
}

### 2
type User
  @model
  @auth(rules: [{ allow: owner, ownerField:"user", provider: userPools }])
  @key(fields: ["user"]) {
  user: String!
  address: String!
  email: String!
}
```

`###1` に記載している `type Mutation functionA` は、Amplify の Lambda リゾルバーと呼ばれる機能です。

- Lambda リゾルバーについてはこちら
    - [Amplify SNS Workshop - timeline機能：@function](https://amplify-sns.workshop.aws/ja/50_follow_timeline/30_function_directive.html)
    - [API (GraphQL) - Configure Lambda resolvers - AWS Amplify Docs](https://docs.amplify.aws/cli-legacy/graphql-transformer/function/)

ここでは、String型の `user` を引数にして、 `functionA-${env}`という名前の Lambda function をキックしています。
返り値はString型です。  

**ここで、
1. Cognito の所属グループが "Admin" であるユーザが、
2. 引数に自分の名前を入れた時のみ、
この Mutation を操作できるように制限をかけたいと仮定します。**

※ Lambda リゾルバーでも、`@auth` ディレクティブを使うことで認証を追加することができます。しかし、Cognito グループに応じた認可や、引数の値に応じた認可はできません。（そのはず）
※ `### 2 ` のように、`@model` を伴う通常のスキーマの場合、`@auth` ディレクティブ の `ownerField` を用いることで引数の値に応じた認可は実現できます。しかし Cognito グループに応じた認可はできません。（そのはず）


# 実装手順
`schema.graphql` を作成後に、`$ amplify push` を実行すると、
以下のパスに、リクエストマッピングテンプレートのリゾルバーが作成されています。
`<project-root>/amplify/backend/api/<api-name>/build/resolvers`

このうち、認証を追加したい処理のもの**のみ**を以下のパスにコピーします。
`<project-root>/amplify/backend/api/<api-name>/resolvers`
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/341404/5f2165f1-307f-59b4-734c-f18f49e5e4bb.png" width="300">
コピー後のリゾルバーを、以下のように書き換えます。（必要に応じて変更してください）

```:Mutation.functionA.req.vtl
## [Start] Stash resolver specific context.. **

#set( $adminRole = "Admin" ) ## Admin グループを設定**
#set( $myRole = $ctx.identity.groups[0] ) ## 自分の所属グループを取得**

#set( $cognitoUserName = $ctx.identity.username ) ## ログインしているCognitoのユーザ名を取得**
#set( $inputUserName = $ctx.args.user ) ## 引数userに入力しているユーザ名を取得**

## 自分の所属グループが"Admin"ではない、もしくは、Cognitoの認証情報と引数userが一致しない場合は処理を終了
#if( $myRole != $adminRole || $cognitoUserName != $inputUserName )
  $util.unauthorized()
#else
$util.qr($ctx.stash.put("typeName", "Mutation"))
$util.qr($ctx.stash.put("fieldName", "functionA"))
{}
#end
## [End] Stash resolver specific context.. **
```

上記サンプルの処理内容は、`## **` でくくっているコメントを参照してください。
意図しないユーザからの操作について、途中で処理を終了して、リクエストされた Mutation が実行されないようにしています。

最後に `$ amplify push` を再度することで、クラウド上のリソースに本リゾルバーの内容が反映されます。
デプロイが完了すると、期待通りに認証がかかった状態になっているはずです。

# お役立ちサイト
AppSync のリゾルバーは奥が深くて難しいです。。。
私の記事と同じものは見当たりませんでしたが、AppSync で認証を実現する方法はいろいろとありそうです。
以下、お役立ちそうなリンクをご参考まで。

- [API (GraphQL) - Overwrite & customize resolvers - AWS Amplify Docs](https://docs.amplify.aws/cli-legacy/graphql-transformer/resolvers/)
- [AWS AppSyncのリゾルバで、AWS CognitoのグループやHTTPリクエストヘッダを使った認可処理を書いてみた - メモ的な思考的な](https://thinkami.hatenablog.com/entry/2019/07/14/215622)
- [AppSync + Cognitoによる認可制御Takumon Blog](https://takumon.com/aws-appsync-auth-with-cognito)
- [リゾルバーでAppSyncの中を開発する - Qiita](https://qiita.com/w2or3w/items/ea217a404becfd0da87f)
- [リゾルバーマッピングテンプレートの概要 - AWS AppSync](https://docs.aws.amazon.com/ja_jp/appsync/latest/devguide/resolver-mapping-template-reference-overview.html)
- [Apache Velocity Engine - User Guide](https://velocity.apache.org/engine/1.7/user-guide.html)