APIGatewayにCognitoオーソライザーをSwaggerで設定する（CloudFrontも少し）

# 背景
AWS使ってサーバーレスで自分用の家計簿的なwebサービスを勉強も兼ねて開発中。メイン機能は大分出来てきて、あとは残課題の対応という形。[前の記事](https://qiita.com/silverbox/items/b3c8a53aa4baa3d02ff8)でCognitoは対応していたが、フロント部分のみ。APIGateway部分にはCognito制御をしていない。せっかくCognito使ってるので、APIGateway側でも制御を入れたい。

# まずAWS側設定
### 手法検討
色々ページを見ていると、CloudFormationでやっているケースあればSwaggerでやってるケースもあり。今回、API部分はSwaggerでやっているのでSwaggerで出来る部分はSwaggerでやりたい。

### やってみた。
色々なページを参考にさせてもらうと、lambdaで認証しているケースが多い。世の中はlambdaで認証チェックするのが主流らしい。Cognitoでやろうという所はあまりないらしい。色々試してみたが、swagger書式でないとか言われたり、エラーは出なかったが、APIGatewayのオーソライザーのタブ見ても新規生成されていなかったり。階層やその表現を色々変えてやったが上手く行かず、手動でやってしまおうかとも考えた。が、結果的に以下の設定でAWSへの反映は出来た。

```yaml:swaggerのオーソライザー定義部分
# トップ階層（componentsセクション配下の方が良いのか？その場合はsecuritySchemes？）
securityDefinitions:
  # このapi_keyは既存部分（Cognito使ったらもう要らないか？）
  api_key:
    type: "apiKey"
    name: "x-api-key"
    in: "header"
  # この名前を各メソッド設定で使用
  myhomeAccountAuthorizer:
    type: "apiKey"
    # 認証に使うトークンを格納するヘッダーのキー
    name: "Authorization"
    # ここを変えたらヘッダー以外でも可能？
    in: "header"
    x-amazon-apigateway-authtype: "cognito_user_pools"
    x-amazon-apigateway-authorizer:
      type: cognito_user_pools
      # 999999999999はアカウントID、ap-northeast-1_hogehogeはCognitoユーザープールID
      providerARNs:
      - "arn:aws:cognito-idp:ap-northeast-1:999999999999:userpool/ap-northeast-1_hogehoge"
      identityValidationExpression: ".*"
```

```yaml:swaggerで各メソッドへオーソライザーを指定する部分例
paths:
  /api/download:
    get:
      produces:
      - "application/json"
      parameters:
        # ・・中略・・
      responses:
        # ・・中略・・
      security:
        - api_key: []
        # で使用したオーソライザーの名前を指定
        - myhomeAccountAuthorizer: []
```

### AWSコンソール側でのオーソライザーテスト
成功すると、AWSコンソール側で、APIGateway => API => オーソライザーで、作成したオーソライザーが表示される。そこでオーソライザーのトークンテストが出来る。
今回、クライアント側ではVue.jsを使用している。javaScriptのawsライブラリ使用中。
session.getAccessToken().getJwtToken() で取得した値を入れてみたが、Unauthorized requestになってしまった。
session.getIdToken().getJwtToken()で取得した値を入れてみたら、AWSコンソール上でのテストは成功。
[参考](https://dev.classmethod.jp/cloud/aws/study-tokens-of-cognito-user-pools/)

# そしてクライアント側設定
もちろんAPIGateway側で認証設定をしたので何もしないとAPI実行で403エラーが返ってくる様になる。ヘッダにAuthorizerキーで、上記IDトークンを指定すればいいはず。開発環境で普通に成功した。

```json:こんなヘッダ
// key, idtokenは変数名
headers: {
  'Content-Type': 'application/json',
  'x-api-key': key,
  'Authorization': idtoken
}
```

これで今まで通りに使える、と思っていたら、CloudFront経由のアクセス（本番）でうまく行かない。

```console:ブラウザのResponseHeaderに出てたエラー
x-amzn-errortype: UnauthorizedException
x-amzn-requestid: ************
x-cache: Error from cloudfront
```
あ、CloudFrontからAPIGatewayに、ヘッダのAuthorizer情報渡ってない・・・
↓ですね。
[【小ネタ】カスタム認証を使うAPI GatewayをCloudFrontの後ろに置いたらAPIを叩けなくなった](https://dev.classmethod.jp/cloud/how-to-put-api-gateway-with-custom-authorization-bihind-cloudfront/)
こちらの神ページを参考に、ヘッダキャッシュ制御の種別をwhitelist指定にして、Authorizerヘッダをwhitelistに追加する事により解消。CloudFrontまわりはCloudFormationでの構築しているのだが、該当部分は以下の形になった。
※この時、ついでに後で使うかもしれないヘッダーを幾つか追加してみたらアクセスエラーになった。使ってもないヘッダーは指定しない方がよさそう。

```yaml:CloudFrontのCloudFormationテンプレート該当部分
        CacheBehaviors:
        - TargetOriginId: ApiGatewayOrigin
          AllowedMethods:
            - GET
            - HEAD
            - OPTIONS
            - PUT
            - PATCH
            - POST
            - DELETE
          ForwardedValues:
            Cookies:
              Forward: all
            # ここを指定する事でwhitelist指定となる。
            Headers:
              - Authorization
            QueryString: True
          ViewerProtocolPolicy: redirect-to-https
          SmoothStreaming: False
          Compress: True
          PathPattern: api/*
          MinTTL: 0
          MaxTTL: 0
          DefaultTTL: 0
```

# 参考にさせてもらったページ
 - [AWS::ApiGateway::Authorizer](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-authorizer.html)
 - [AWS::CloudFront::Distribution Cookies](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-cookies.html)
 - [AWS::CloudFront::Distribution ForwardedValues](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-forwardedvalues.html)
 - [x-amazon-apigateway-authorizer オブジェクト](https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/api-gateway-swagger-extensions-authorizer.html)
 - [ユーザープールのトークンの使用](https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-with-identity-providers.html)
 - [Cognitoから払い出されたIdTokenをAPI Gateway カスタムオーソライザーのLambda(Python3.6)で検証する方法](https://dev.classmethod.jp/cloud/aws/verify_cognit_idtoken_by_apig_custom_auth/)
 - [AWS Cognitoにサインインしないと見れないLambdaを作る](https://qiita.com/poruruba/items/86668c9c63844e1afd32)
 - [Amazon API Gateway の Custom Authorizer を使い、OAuth アクセストークンで API を保護する](https://qiita.com/TakahikoKawasaki/items/b372ab49da0a9aedb76a)
 - [AWS APIGateway with Cognito Authorizer defined in OpenAPI Spec](https://stackoverflow.com/questions/56337629/aws-apigateway-with-cognito-authorizer-defined-in-openapi-spec)
 - [【小ネタ】カスタム認証を使うAPI GatewayをCloudFrontの後ろに置いたらAPIを叩けなくなった](https://dev.classmethod.jp/cloud/how-to-put-api-gateway-with-custom-authorization-bihind-cloudfront/)
 - [Cognitoのサインイン時に取得できる、IDトークン・アクセストークン・更新トークンを理解する](https://dev.classmethod.jp/cloud/aws/study-tokens-of-cognito-user-pools/)