プログラマーのメモ書き

伊勢在住のプログラマーが気になることを気ままにメモったブログです

API Gateway の Lambda プロキシ統合について

blog.mori-soft.com

を書いた当時は、API Gateway のバックエンドとして Lambda を使う場合、Lambda 関数を定義して、統合リクエスト・統合レスポンスの設定をしていました。 でも、この少し後に、 Lambda プロキシ統合という便利な機能がリリースされていたことを知りました。

リリース時の記事がこちら。

Amazon API Gateway に API 設定を簡素化する 3 つの新機能を追加

(ずいぶん前ですが)この Lambda プロキシ統合を試したときに感じたことをメモしておきます。 ・・・今回は半分愚痴ですので、軽く流してください。

実現方法

自分では新しく調べたことは何もなくて、下記記事のとおり行ったら、すんなりとできました。

qiita.com

まとめていただいて、ありがたいです。

ドキュメントが今一つよくわからない

で、この記事で書きたかったのは、こちらのほうです。

結論からいうと、API Gateway の Lambda プロキシ統合の部分のドキュメントはちょっとわかりにくいかと思います。

まず、用語を整理しましょう。API Gateway で Lambda 関数を使う場合のことを Lambda 統合 と呼んでいるようです。 で、その Lambda 統合の中に

  • Lambda プロキシ統合
  • Lambda カスタム統合

があるという形のようです。こちらの記事に説明がありました。

docs.aws.amazon.com

Lambda カスタム統合というのは、既存の方法で、自前で統合リクエスト・統合レスポンスのマッピングを行うという方法です。

で、前述の新機能のリリース記事にもあるように、 API Gateway の便利な機能として

  • greedy (グリーディー)パス
  • ANY メソッド
  • Lambda プロキシ統合

の3つがあります。 このうち、greedyパスで定義されるAPIリソースのことを、『プロキシリソース』と呼んでいるようです。 こちらの記事でそのことが書かれています。

docs.aws.amazon.com

ここで、プロキシと名の付くのが2つ出てくるので非常にややこしい。 もっとも、ドキュメントをよく読めば、上記の3つの機能は独立した機能であり、個別に使うことが可能、とあるのですが、このことがなかなか伝わってきませんでした。

サンプル

あと、Lambdaプロキシ統合について書かれているサンプルが、プロキシリソースを対象にしたものばかりで、単にメソッドに対して Lambdaプロキシ統合を行いました、 というのがないようです。

後者のドキュメントをよくよめばいいんでしょうが、サンプル見るほうが手っ取り早かったりもしますしね。

ごく簡単な設定だし、四の五の言わず Lambdaプロキシ統合を試せば、十分、分かるんですけど、ドキュメントはもう少しなんとかしてほしいものです。

補足

Lambdaプロキシ統合時のCORSを有効にする方法について、ドキュメントではここで触れてます。

API Gateway リソースの CORS を有効にする - Amazon API Gateway

Lambdaプロキシ統合の場合、バックエンド(つまりLambda関数)内でレスポンスヘッダを指定してやる必要があるということを覚えておけば良さそうです。

Cognito ユーザープールのトークンと保管場所について

Cognito ユーザープール、なかなか便利そうですね。

blog.mori-soft.com

上記を元に、いろいろと試してみて、気づいたことがいくつかあったのでまとめておきます。

更新トークンの有効期限

ユーザープールで認証をした際にはトークンが3種類発行されます。

docs.aws.amazon.com

そのうちの更新トークン(リフレッシュトークン)の有効期限は、ユーザープール作成時に指定できるのですがデフォルトだと30日になります。

ユーザープールのトークンの使用 - Amazon Cognito

もし、変更したい場合は、Cognito ユーザープール のコンソールを開いて、『アプリクライアント』->『詳細を表示』とすると

f:id:junichim:20180817224001p:plain

『トークンの有効期限を更新(日)』とあるので、ここで適切な日数に変更できます。

ブラウザのストレージ

また、ユーザープールで認証した際に、これらのトークンはローカルストレージに保存されるようです。なので、明示的にログアウトせず、ブラウザを落としただけだと、更新トークンが活きていて、簡単にセッションを貼りなおせるという問題があるそうです。

qiita.com

これへの対策として、トークンをセッションストレージに保存する、というのがあるようです。

確かめてみましょう。 ブラウザ(ここでは Firefox を使いました)で一度Cognito ユーザープールを使ったアプリにログインして、ローカルストレージを見ると

f:id:junichim:20180817225836p:plain

のようにローカルストレージに各種トークンが保存されていることがわかります。 ログアウトせずに一旦ブラウザを落として、再度同じサイト(ここでは http://localhost:9000 )にアクセスすると、ログイン前なのに上記と同じトークンの値が存在しているのを確認できました(画像貼っても同じなので省略しました)。

これだと、更新トークン(デフォルトで有効期限が30日)を使えば、IDトークン、アクセストークンの再取得ができちゃいますね(処理書く必要はありますが)。

ということで、ブラウザ側でユーザープールにアクセスする際に、

morisoftsample.poolData = {
    UserPoolId: 'ap-northeast-1_xxxxxxxxxxxxxx',
    ClientId: 'yyyyyyyyyyyyyyyyyyyyyyy',
    Storage: sessionStorage
};
morisoftsample.UserPool = new AmazonCognitoIdentity.CognitoUserPool(morisoftsample.poolData);

のようにしたら、セッションストレージに保存されるとのことです。

こちらも試してみます。

f:id:junichim:20180817230406p:plain

今度はセッションストレージに格納されていることが分かります。 このままログアウトせずに、一度ブラウザを落として、再度立ち上げると、

f:id:junichim:20180817230547p:plain

のように、きれいに消えてなくなっています。

これだと少しは安心ですね。

Cognito ユーザープール : メールアドレスの変更と確認について

Cognito ユーザープール、少しづつですが使ってみています。

今回はメールアドレスの変更をやってみました。基本的には、ドキュメントにある通り処理をすればOKです。

メールアドレスも属性の一つなので、属性の更新を行えば更新できます。 ただ、同じページのサンプルに『属性の確認』というのがあります。

言葉からすると、きっとメールアドレスを変更した場合はこれで再度確認をしないといけないんだろうな、と想像がつきます。が、いまひとつよくわかりません。 なので、いろいろと試した結果を、自分用のメモとしてまとめておきます。

前提

いま試しているユーザープールの設定は、

  • エイリアスは指定していない
  • ログインは、ユーザー名のみを許可
  • 属性として、 メールアドレス、preferred_username を追加

となっています。このあたりの設定が異なると、以下の挙動も違うかもしれませんのでご注意ください(特に確認していません)。

メールアドレスの変更で起きること

まず試しにメールアドレスを変更してみます。 変更前は、こんな感じだったのが、

f:id:junichim:20180817084816p:plain

メールアドレスを変更すると、こんな感じになりました(属性の確認は未実施)。

f:id:junichim:20180817084917p:plain

  • アカウントそのものは CONFIRMED の状態のまま
  • email_verified のステータスが false となる
  • アカウントに対するメールアドレスは変更済み(黒塗りでわからないですが、変わってます)

つまり、確認され有効なアカウントに対して、メールアドレスを変更すると、メールアドレスの確認が取れていないが、登録メールアドレスそのものは変更されている、という状態になります。 また、メールアドレスの変更時は、存在しないメールアドレスや別のアカウントで使っている重複するメールアドレスを指定しても変更ができました。なお、重複するメールアドレスの扱いはエイリアス指定があるときっと挙動が変わってくると推測されるところです。

この状態(メールアドレスだけ変更して、属性の確認は未実施)だと、なにができてできないかを調べたところ

  • ログイン:できる
  • パスワードのリセット:できない
  • メールアドレスの再変更:できる

となりました。登録メールアドレスが未確認の状態でも、パスワードでログイン等をする分には問題ないようです。 また、間違ったメールアドレスを入力してしまった、という場合でも、再度メールアドレスの変更ができるのでなんとかなるようです。

ということで、この状態でこまるのが、パスワードのリセットですね。 下記の記事にも、困ってしまった話が載っていました。

wp-kyoto.net

メールアドレスの確認

では、ということで、『属性の確認』を行えば、メールアドレスの確認ができるのだろうと考え、メールアドレスの確認処理を実装してみました。 となるのですが、公式のサンプルがちょっとわかりにくかったので、少し自分で修正してみました。

公式のサンプルだと、確認コードの取得(登録メールアドレスに送信)と確認コードの検証が一緒になったサンプルになっているようです。 なので、

morisoftsample.getAttributeVerificationCode = function(cognitouser) {
    return new Promise(function(resolve, reject) {
        cognitouser.getAttributeVerificationCode('email', {
            onSuccess: function(result) {
                console.log('success getAttributeVerificationCode');
                resolve(result);
            },
            onFailure: function(err) {
                console.log('failed getAttributeVerificationCode: ' + JSON.stringify(err));
                reject(err);
            }
        });
    });
}

という確認コードの取得(登録メールアドレスに送信)する関数と

morisoftsample.verifyAttribute = function(cognitouser, vericode) {
    return new Promise(function(resolve, reject) {
        cognitouser.verifyAttribute('email', vericode, {
            onSuccess: function(result){
                console.log('email verification success');
                resolve(result);
            },
            onFailure: function(err) {
                console.log('email verification failed');
                reject(err);
            }
        });
    });
}

という、取得した確認コードでメールアドレスをverifyする関数に分離してみました。 なお、上記のサンプルでは、それぞれの処理を Promise でラップしていますので、Promise を使わない場合は適当に読み替えてください。あと、cognitouser というのは、

で取得したログイン済みのユーザーになります。

この関数を呼び出すように、クライアント側のjavascriptを書くと、めでたく、確認コードの取得と確認コードのverifyを分けて扱うことができました。

ちなみに、Githubのリポジトリのほうのサンプルの Use Case 6 に少しだけ説明が載っていますので、ご参考までに。

github.com