[ Serverless ] Cognito、S3、Lambdaで認証機能付きのWebサイトを作ってみました<トラブルシューティング編>

#概要
　先日は、CognitoのUser Poll&Identity Pool、S3、Lambdaを使って、認証付きのWebサイトを作ってみました。
↓↓↓↓↓↓↓↓詳細は下記の投稿にて確認してください↓↓↓↓↓↓↓↓↓↓
[[ Serverless ] Cognito、S3、Lambdaで認証機能付きのWebサイトを作ってみました](http://qiita.com/jobbin/items/d2fc0f714eb1f1cfc965)
<img width="881" alt="スクリーンショット 0028-07-02 0.31.50.png" src="https://qiita-image-store.s3.amazonaws.com/0/117156/6d143776-fc90-b36b-c24b-70f64433759a.png">

　AmazonのFull Managedサービスをフル活用し、しかもEC2を利用せずに作れましたので、まさに**Serverless**ですね。
　前回書いた内容は、上記の図の① ~ ⑥の内容です。⑦ ~ ⑨内容も書こうと思いましたが、元々① ~ ⑥と⑦ ~ ⑨の仕組みを分けて考えても問題がないこと(S3イベントで連携しているだけ)と、S3とLambdaの連携は簡単にできますし、LambdaとSlackの連携については、少し前に私が書いたもう一つの投稿↓↓↓↓↓↓↓↓↓↓
[【Lambda & CloudWatchEvents & Slackで 長期的にログインしていないIAMユーザを検知・通知する】](http://qiita.com/jobbin/items/21737cc258cf4268b176)
に設定方法(python版)を説明しましたので、今回は⑦ ~ ⑨の説明を省略します。それの代わりに、①から⑥までの間に、下記挙げたエラーメッセージから、頭が悩まされていた問題(特に**Cognito認証・認可まわり**)を皆さんと共有したいと思います。

- NotAuthorizedException: Missing credentials in config
- Upload FailedNetwork Failure
- Upload FailedMissing credentials in config

今振り返ってみると、何でそんなことにすら気ついていなかったのかという気持ちですが、ハマっていた時は本当に頭が悩まされました。

#問題1: NotAuthorizedException: Missing credentials in config
##事象
![エラー.png](https://qiita-image-store.s3.amazonaws.com/0/117156/f0028857-2307-1350-41c9-524a2ad04aff.png)

　前回の投稿内容の最後に、残り１つの問題を解決していないと皆さんに伝えました。まさにこの問題です。ユーザログインする際に、ログインボタンを押すと、必ず上記のエラーが一度出てきます。もう一回ログインボタンを押したら、ログインできちゃいます。
　エラーの内容に表示された通りですが、configにcredentialsの情報が書かれていないよと怒られています。
　前回私が書いた投稿 [[ Serverless ] Cognito、S3、Lambda ...](http://qiita.com/jobbin/items/d2fc0f714eb1f1cfc965)を見た方であれば分かると思いますが、当時webform.htmlのコードには下記のように書いてあります。
<img width="1664" alt="17.pic_hd.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/117156/b829e7db-7192-25a5-fe6b-4792a4b82549.jpeg">

　Missing credentials in configと怒られても、「AWS.CognitoIdentityCredentials( )でちゃんとかいてんじゃん！」という気持ちで、バグじゃねかよと思いました。まぁ、本当にバグであれば、しょうがないかもしれませんけど、思い込みはだめなので、少し時間をかけて、調べてみました。

##原因
　エラーメッセージの通りですが、configにcredentialsの情報がないということです。
　ですので、accessKeyIdとsecretAccessKeyを書かないといけないですね!


##解決策
**上記の図に赤線で囲まれている部分のコードを下記のコードに書き換えることで、解決**

```
      // Cognito User Pool Id
      AWSCognito.config.region = 'us-east-1';
      AWSCognito.config.credentials = new AWS.CognitoIdentityCredentials({
        IdentityPoolId: 'us-east-1:a999fc0e-b*************5d4fb6ffa'
      });
      AWS.config.region = 'us-east-1';
      AWS.config.credentials = new AWS.CognitoIdentityCredentials({
          IdentityPoolId: 'us-east-1:a999fc0e-b*************5d4fb6ffa'
      });

      AWS.config.credentials.get(function(){
          var accessKeyId = AWS.config.credentials.accessKeyId;
          console.log("accessKeyId: " + AWS.config.credentials.accessKeyId);
          var secretAccessKey = AWS.config.credentials.secretAccessKey;
          console.log("secretAccessKey: " + AWS.config.credentials.secretAccessKey);
          var sessionToken = AWS.config.credentials.sessionToken;
          console.log("sessionToken: " + AWS.config.credentials.sessionToken);
      });
```
　ブラウザを更新してから、Chromeのデベロッパーツールで見ると、ちゃんとcredentialsの情報を取れていることが分かりますね。
<img width="1756" alt="11.pic_hd.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/117156/b4c93652-e329-cf1d-daa1-873047b7b5d1.jpeg">

　何をやっているかと言いますと、**認証されていないユーザが** 指定したIdentity Poolにアクセスし、**認証されていないユーザに与えるCredential情報**をもらって、configに書き込んでいます。ですので、下記の図のように、Identity PoolのUnauthenticated identitiesのボックスにチェックを入れてください
<img width="1720" alt="12.pic.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/117156/816b1b9c-e561-1057-e725-834c617ff795.jpeg">

　ちなみに、「Enable access to unauthenticated identities」にチェックを入れないと、**CredentialsError: Missing credentials in config**というエラーが出ました。

##補足
　[amazon-cognito-identity-jsのREADME.md(Use case 1)](https://github.com/aws/amazon-cognito-identity-js)から、もう１つ解決方法を見つけました。(個人的に非常に気持ち悪い解決方法だと思います)

赤線で囲まれているコードの下の行に、下記のコードをそのまま書けば、解決できます。
<Font color="red">
 AWSCognito.config.update({accessKeyId: 'anything', secretAccessKey: 'anything'})
</font>
※　適当にaccessKeyIdとsecretAccessKeyを書けばいい、何それを思いませんか

#問題2: Upload FailedNetwork Failure
##事象
<img width="1980" alt="13.pic.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/117156/b6b77849-273a-4406-b128-fb089d2dc5b0.jpeg">
エラーメッセージ:
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. 

##原因
　原因は非常にシンプルで、私のミスでした。
　何をやったかとい言いますと、セキュリティ強化するために、BucketのCORSを下記のように設定しました。
<img width="1580" alt="14.pic_hd.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/117156/bdc6e7e4-ecf4-b315-3040-92b628964f66.jpeg">

\<AllowedOrigin>には静的ホスティングされているエンドポイント**Bucket名.s3-website-us-east-1.amazonaws.com**を指定しています...が、検証の時は、S3に上げたhtmlファイルをそのままクリックして、アクセスしていたので、URLは**s3.amazonaws.com/Bucket名/webform.html**になっていました。
　S3周りのポリシー設定とかCORSとかは、正直に言って、自分がまだよく分かりませんが、これから、色々いじってみないとだめですね。

##解決策
　エンドポイントのURLでアクセスすれば、エラーが出なくなったので、解決しました。

#問題3: Upload FailedMissing credentials in config
##事象
<img width="830" alt="15.pic.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/117156/97f07cd3-faa4-04fb-19ae-e48b0206092d.jpeg">
エラーメッセージ
error in autheticatig AWSNotAuthorizedException: Invalid login token. Missing a required claim: aud

##原因と解決策
　解決策を先に書きますと、下記 webform.htmlに赤線で囲まれている部分( **result.getIdToken( ).getJwtToken( )** )に書き換えると、エラーを解消しました。
<img width="1610" alt="16.pic_hd.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/117156/afadabc3-11e8-a5a2-1054-d921eb71461b.jpeg">

ユーザが一度認証して、CognitoのUser Poolから下記３つの情報を取得しています。

1. accessToken
2. idToken
3. refreshToken

　私は最初の頃、**result.getIdToken( ).getJwtToken( )**ではなく、**result.accessToken( ).getJwtToken( )**を書きましたが、CognitoのIdentity PoolはaccessTokenではなく、**idToken**の情報でユーザが認証されているかどうかを確認し、crendential情報を払い出しています。


#まとめ
　今回、Cognito、S3を使って、ServerlessのWebサイトを作ることによって、ある程度Cognito認証・認可周りの知識を得られました。なかなか奥深いなと思いながらも、少しずつCognitoの理解が深まったのではないかと思います。
　この記事で、少しでも皆さんにお役に立てれば嬉しいです。もし、何か間違った情報を書いてしまったら、是非ご指摘ください。

#余談
　Serverless、ChatOpsに結構興味がありますが、仕事では、AWSの社内利用において、セキュリティ管理統制などもやっています。今後、セキュアにAWSを利用するためにどうすればいいのかについても、できる範囲で、皆さんに共有したいと思っています。