[Amplify]APIGatewayのAuthorizerにCognitoを指定する

## 概要
AmplifyでOverride機能を利用してAPIGatewayのAuthorizerにCognitoを指定する方法をご紹介します。

## 課題
2022/3/10現在、Amplify CLIでは、APIGatewayのAuthorizerにIAMしか利用できません。AWSのコンソールからCognitoを指定したとしても更新した際にテンプレートで上書きされてしまい、設定したAuthorizerの情報が消えてしまいます。
そのため、Override機能を利用してAPIのCFnテンプレートを修正する必要があります。

## 手順
### APIGateway・Cognitoのoverride.ts作成
CLIで以下のコマンドを実行し、override.tsを作成します
```
amplify override auth
amplify override api
```

### override.tsの編集
Authでは、CognitoユーザープールのIDをエクスポートします。
エクスポート名は重複が許されないため、環境名を取得してユニークにしています。しかし、デフォルトではバグで取得できないため迂回しています。詳細は以下の記事をご覧ください。
[Amplifyのoverride.tsで環境名を取得する](https://qiita.com/naniwadari/items/7944b31ce52f85646e2c)
```javascript:amplify/backend/auth/[Auth名]/override.ts
import { AmplifyAuthCognitoStackTemplate } from '@aws-amplify/cli-extensibility-helper';

export function override(resources: AmplifyAuthCognitoStackTemplate) {
  // override.tsでenv名を参照することはできない(バグ)。そのためamplify-metaからenv名を取得している
  // https://github.com/aws-amplify/amplify-cli/issues/9063
  const amplify_meta_json = require('amplify-meta.json')
  const env_name = amplify_meta_json.providers.awscloudformation.StackName.split("-").slice(-2, -1).pop()
  const export_user_pool_id = {
      description: "cognito user pool id",
      value: resources.userPool.ref,
      exportName: `exportUserPoolId-${env_name}`
  }
  resources.addCfnOutput(export_user_pool_id, "AuthCognitoUserPoolId")
}
```

AuthでエクスポートしたユーザープールIDを利用してAuthorizerを設定します。
```javascript:amplify/backend/api/[API名]/override.ts
import { AmplifyApiRestResourceStackTemplate } from '@aws-amplify/cli-extensibility-helper';

export function override(resources: AmplifyApiRestResourceStackTemplate) {
  const amplify_meta_json = require('amplify-meta.json')
  const env_name = amplify_meta_json.providers.awscloudformation.StackName.split("-").slice(-2, -1).pop()
  resources.restApi.body = {
    ...resources.restApi.body,
    "securityDefinitions": {
      "sigv4": {
        "type": "apiKey",
        "name": "Authorization",
        "in": "header",
        "x-amazon-apigateway-authtype": "awsSigv4"
      },
      "Cognito": {
        "type": "apiKey",
        "name": "Authorization",
        "in": "header",
        "x-amazon-apigateway-authtype": "cognito_user_pools",
        "x-amazon-apigateway-authorizer": {
          "type": "cognito_user_pools",
          "providerARNs": [
            {
              "Fn::Join": [
                "",
                [
                  "arn:aws:cognito-idp:",
                  {
                    "Ref": "AWS::Region"
                  },
                  ":",
                  {
                    "Ref": "AWS::AccountId"
                  },
                  ":userpool/",
                  {
                    "Fn::ImportValue": `exportUserPoolId-${env_name}`
                  }
                ]
              ]
            }
          ]
        }
      }
    },
  }

  //Authorizerはリソースの各メソッドに設定する必要がある
  let paths = resources.restApi.body.paths
  Object.keys(paths).forEach((key) => {
    let path = paths[key]["x-amazon-apigateway-any-method"]
    path.parameters = [
      ...path.parameters,
      {
        name: "Authorization",
        in: "header",
        required: false,
        type: "string"
      }
    ]
    path.security = [
      {
        "Cognito": []
      }
    ]
  });
}
```

### まとめ
上記設定でAuthorizerにCognitoを使用することができます。
AmplifyのOverride用ヘルパーの情報が少なく、記載方法にとまどいましたのでどなたかの参考になれば幸いです。