血と汗となみだを流す

クラウドエンジニアになるための修業の場

Javascriptがわからないので少しずつコード読んで和解していく(cognitoと連携したユーザ登録)

概要

  • サーバーレスウェブアプリケーションの構築をやっていた
  • 「モジュール2 ユーザ管理」通りにやっていたら、あまりにも簡単に認証ページが作成できた
  • が、AWS CognitoとJavascriptがどう動いているか全くわからず・・・
  • そろそろちゃんとJavascriptと向き合わないと行けないなと思い、コードを読んで理解することにした

コード

コード読んだ対象

  • ユーザ登録を行っているregister.htmlで読み込んでるjavascript

長くなってしまったので、先にユーザ登録フローのまとめ

  • register.htmlで登録フォーム表示
    • メールアドレス
    • パスワード
    • パスワード確認
  • submitすると js/cognito-auth.js のhandleRegister()が実行される
  • handleRegister()で以下の処理を実行
    • フォームの値を取得する
    • ユーザ登録処理成功/失敗時の処理を定義
    • ユーザ登録処理(register())を実行
  • register()で以下の処理
    • ユーザの登録データオブジェクトを生成
    • cognito-sdkのAmazonCognitoIdentityクラスを使ってcognitoにユーザを登録する

register.html

  • SignUpページ
  • メールアドレスと、パスワード2回入れる
  • formのデータをsubmitしたときの処理はどこかのJSに書いてあるっぽい
  • JS読み込みは以下の箇所
<script src="js/vendor/jquery-3.1.0.js"></script>
<script src="js/vendor/bootstrap.min.js"></script>
<script src="js/vendor/aws-cognito-sdk.min.js"></script>
<script src="js/vendor/amazon-cognito-identity.min.js"></script>
<script src="js/config.js"></script>
<script src="js/cognito-auth.js"></script>
  • js/vendor/ 配下はおそらくjQuery、bootstrapと、cognitoのSDK用のライブラリ
  • js/config.js はcognitoに接続するためのプールIDやアプリクライアントIDの設定用
  • js/cognito-auth.js が怪しいので見ていく

bootstrap

js/cognito-auth.js が呼ばれるときに実行される処理ってどれ?

  • 関数の最初と最後だけ抜き出してみる
(function scopeWrapper($) {
    ..[省略]..
}(jQuery));
  • functionが()でかこまれてるのは「即時関数」にしている
    • 即時関数: 関数を定義すると同時に実行する
    • つまり、js/cognito-auth.jsが読み込まれると同時に上記関数は実行される
  • 後述の「userPoolオブジェクト生成」「onDocReady()」とかもここで実行されている
  • 引数で「jQuery」が渡されて、引数定義の「$」がjQueryオブジェクトとなる
    • つまり、「function scopeWrapper」内の「$」はjQueryオブジェクト

userPoolオブジェクト生成

  • js/cognito-auth.jsの呼び出し時にオブジェクト生成している
  • インラインでやってることを推測しながらコメントを追加
(function scopeWrapper($) {
    // サインイン用のページ
    var signinUrl = '/signin.html';

    // config.jsからAWS Cognitoユーザプールの認証情報を取得
    var poolData = {
        UserPoolId: _config.cognito.userPoolId,
        ClientId: _config.cognito.userPoolClientId
    };

    // userPoolオブジェクト定義
    var userPool;

    // ユーザプールID、アプリクライアントID、リージョンがセットされてなかったら、
    // register.html内にある「noCognitoMessage」部分を表示(エラーメッセージ的なやつ)
    if (!(_config.cognito.userPoolId &&
          _config.cognito.userPoolClientId &&
          _config.cognito.region)) {
        $('#noCognitoMessage').show();
        return;
    }

    // 認証情報から、Cognitoユーザプールオブジェクトを生成
    userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
  • 大まかに
    • config.jsの情報からユーザプールオブジェクトを生成

js/cognito-auth.jsのonDocReady()メソッド

  • formのsubmitの挙動に関する定義っぽい箇所があった
    /*
     *  Event Handlers
     */

    $(function onDocReady() {
        $('#signinForm').submit(handleSignin);
        $('#registrationForm').submit(handleRegister);
        $('#verifyForm').submit(handleVerify);
    });
  • submit()メソッド

handleRegister()メソッド

  • submitされると実行される
  • インラインでやってることを推測しながらコメントを追加
    // 画面上のイベントを引数でもらう
    function handleRegister(event) {

        // 「registrationForm」にあったパラメータの値を変数に入れる
        var email = $('#emailInputRegister').val(); // メールアドレス
        var password = $('#passwordInputRegister').val();   // パスワード
        var password2 = $('#password2InputRegister').val(); // パスワード(確認用)

        // 後ろにあるregister()メソッドの引数として、register()が成功したときに実行するfunctionを定義している
        var onSuccess = function registerSuccess(result) {

            // おそらく成功した場合にresultにユーザ情報が入っている
            var cognitoUser = result.user;

            // コンソールにユーザ名を表示
            console.log('user name is ' + cognitoUser.getUsername());

            // 「ユーザ登録成功してメール送ったから確認しろよ、認証コード入っているから、スパムフォルダとか見てね」的な確認メッセージを定義
            var confirmation = ('Registration successful. Please check your email inbox or spam folder for your verification code.');

            // 確認メッセージがあったら(っていうか上で定義しているから絶対あるのでは・・・)、verify.htmlに画面遷移する
            if (confirmation) {
                window.location.href = 'verify.html';
            }
        };

        // 後ろにあるregister()メソッドの引数として、register()が失敗したときに実行するfunctionを定義している
        var onFailure = function registerFailure(err) {

            // エラーを表示
            alert(err);
        };

        // eventの抑制。おそらくsubmitの処理をここで止めている。
        // register()にわたす引数(function)内で画面遷移を行っているから、submitによる画面遷移処理が不要なんじゃないかな?
        event.preventDefault();

        // パスワードとパスワード確認が同一だったら
        if (password === password2) {

            // register()メソッド(おそらくユーザ登録処理)に処理を渡す
            // 引数は
            //   email: 入力されたメールアドレス
            //   password: 入力されたパスワード
            //   onSuccess: 成功時に実行するfunction
            //   onFailure: 失敗時に実行するfunction
            register(email, password, onSuccess, onFailure);
        } else {

            // パスワード一致しなかったらアラート出して終わり(submitは止められているので元の画面のまま)
            alert('Passwords do not match');
        }
    }
  • 大まかに書くと、以下のような感じ
    • フォームの値を取得
    • ユーザ登録処理(register)の成功時に実行するfunctionを定義する
    • ユーザ登録処理(register)の失敗時に実行するfunctionを定義する
    • submitを止める
    • パスワード一致確認して、ユーザ登録処理を実行

register()メソッド

  • インラインでやってることを推測しながらコメントを追加
    /*
     * Cognito User Pool functions
     */

    // 引数4つ受け取る
    //   email: 入力されたメールアドレス
    //   password: 入力されたパスワード
    //   onSuccess: 成功時に実行するfunction
    //   onFailure: 失敗時に実行するfunction
    function register(email, password, onSuccess, onFailure) {

        // メールアドレスデータの連想配列
        var dataEmail = {
            Name: 'email',
            Value: email
        };

        // AmazonCognitoIdentityクラスにメールアドレスデータをセットしたオブジェクトを生成してるっぽい
        // @see https://github.com/aws-amplify/amplify-js/blob/bfa477662591bbd7a92bbb8486778924589e4adb/packages/amazon-cognito-identity-js/es/CognitoUserAttribute.js#L21
        var attributeEmail = new AmazonCognitoIdentity.CognitoUserAttribute(dataEmail);

        // userPoolオブジェクト(後述)のsignUpメソッドに以下の引数を渡している
        // メソッド自体はここか? @see https://github.com/aws-amplify/amplify-js/blob/bfa477662591bbd7a92bbb8486778924589e4adb/packages/amazon-cognito-identity-js/es/CognitoUserPool.js#L93
        //   {string} username: 入力されたメールアドレス(email)
        //   {string} password: 入力されたパスワード(password)
        //   {(AttributeArg[])=} userAttributes: 登録するユーザの属性情報配列([attributeEmail])
        //   {(AttributeArg[])=} validationData: バリデーションデータを入れるっぽいが、ここはnull
        //   {nodeCallback<SignUpResult>} callback: コールバック用の関数定義(function signUpCallback)
        userPool.signUp(email, password, [attributeEmail], null,
            function signUpCallback(err, result) {
                if (!err) {
                    // エラーじゃなかったら成功時に実行するfunctionを実行
                    onSuccess(result);
                } else {
                    // エラーだったら成功時に実行するfunctionを実行
                    onFailure(err);
                }
            }
        );
    }
  • 大まかに書くと、以下のような感じ
    • 引数からユーザ情報に登録するデータオブジェクトを作成
    • AmazonCognitoIdentityオブジェクトに登録データとコールバック用の関数を渡す
    • AmazonCognitoIdentityがsignUp処理を実行して、結果によってfunctionを実行
  • cognitoへの登録はAmazonCognitoIdentityがやってくれる模様

本日javascriptと和解できたところ

  • functionを()で囲むと即時関数となって、function定義と同時に実行される
  • bootstrapはCSSフレームワークで、jqueryが必要
  • submit()メソッドで、submitされた時に実行できるfunctionを定義できる
  • Callback関数の形式
    • 成功時/失敗時に実行するfunctionを変数に入れておいて引数で渡す(なるほどcallback)
  • Cognito-SDKのAmazonCognitoIdentityがいろいろやってくれる(がマニュアルが見つからない)