Edited at

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

More than 3 years have passed since last update.


概要

 先日は、CognitoのUser Poll&Identity Pool、S3、Lambdaを使って、認証付きのWebサイトを作ってみました。

↓↓↓↓↓↓↓↓詳細は下記の投稿にて確認してください↓↓↓↓↓↓↓↓↓↓

[ Serverless ] Cognito、S3、Lambdaで認証機能付きのWebサイトを作ってみました

スクリーンショット 0028-07-02 0.31.50.png

 AmazonのFull Managedサービスをフル活用し、しかもEC2を利用せずに作れましたので、まさにServerlessですね。

 前回書いた内容は、上記の図の① ~ ⑥の内容です。⑦ ~ ⑨内容も書こうと思いましたが、元々① ~ ⑥と⑦ ~ ⑨の仕組みを分けて考えても問題がないこと(S3イベントで連携しているだけ)と、S3とLambdaの連携は簡単にできますし、LambdaとSlackの連携については、少し前に私が書いたもう一つの投稿↓↓↓↓↓↓↓↓↓↓

【Lambda & CloudWatchEvents & Slackで 長期的にログインしていないIAMユーザを検知・通知する】

に設定方法(python版)を説明しましたので、今回は⑦ ~ ⑨の説明を省略します。それの代わりに、①から⑥までの間に、下記挙げたエラーメッセージから、頭が悩まされていた問題(特にCognito認証・認可まわり)を皆さんと共有したいと思います。


  • NotAuthorizedException: Missing credentials in config

  • Upload FailedNetwork Failure

  • Upload FailedMissing credentials in config

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


問題1: NotAuthorizedException: Missing credentials in config


事象

エラー.png

 前回の投稿内容の最後に、残り1つの問題を解決していないと皆さんに伝えました。まさにこの問題です。ユーザログインする際に、ログインボタンを押すと、必ず上記のエラーが一度出てきます。もう一回ログインボタンを押したら、ログインできちゃいます。

 エラーの内容に表示された通りですが、configにcredentialsの情報が書かれていないよと怒られています。

 前回私が書いた投稿 [ Serverless ] Cognito、S3、Lambda ...を見た方であれば分かると思いますが、当時webform.htmlのコードには下記のように書いてあります。

17.pic_hd.jpg

 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の情報を取れていることが分かりますね。

11.pic_hd.jpg

 何をやっているかと言いますと、認証されていないユーザが 指定したIdentity Poolにアクセスし、認証されていないユーザに与えるCredential情報をもらって、configに書き込んでいます。ですので、下記の図のように、Identity PoolのUnauthenticated identitiesのボックスにチェックを入れてください

12.pic.jpg

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


補足

 amazon-cognito-identity-jsのREADME.md(Use case 1)から、もう1つ解決方法を見つけました。(個人的に非常に気持ち悪い解決方法だと思います)

赤線で囲まれているコードの下の行に、下記のコードをそのまま書けば、解決できます。



AWSCognito.config.update({accessKeyId: 'anything', secretAccessKey: 'anything'})



※ 適当にaccessKeyIdとsecretAccessKeyを書けばいい、何それを思いませんか


問題2: Upload FailedNetwork Failure


事象

13.pic.jpg

エラーメッセージ:

Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.


原因

 原因は非常にシンプルで、私のミスでした。

 何をやったかとい言いますと、セキュリティ強化するために、BucketのCORSを下記のように設定しました。

14.pic_hd.jpg

<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


事象

15.pic.jpg

エラーメッセージ

error in autheticatig AWSNotAuthorizedException: Invalid login token. Missing a required claim: aud


原因と解決策

 解決策を先に書きますと、下記 webform.htmlに赤線で囲まれている部分( result.getIdToken( ).getJwtToken( ) )に書き換えると、エラーを解消しました。

16.pic_hd.jpg

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


  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を利用するためにどうすればいいのかについても、できる範囲で、皆さんに共有したいと思っています。