AWS Cognitoの認証機能をAPI Gatewayの呼び出しに利用する | AWSサーバレス構築

参考にするチュートリアル

英語版チュートリアル(新しめ)
日本語チュートリアル(古いけど有益)
Javascriptコードの在り処

また、事前に当ブログ記事「簡易チュートリアル」を参照のこと。

JavaScript

AWS SDK

amazon-cognito-identity.min.js と aws-cognito-sdk.min.js を、公式チュートリアルのコードから拝借する。(ventorフォルダ)

config.js

Cognitoで作成したユーザープールの「プールID」「アプリクライアントID」「リージョン」を入力する。

window._config = {
    cognito: {
        userPoolId: 'プールID',
        userPoolClientId: 'アプリクライアントID',
        region: 'リージョン'
    },
    api: {
        invokeUrl: 'APIGatewayでデプロイしたステージのURL'
    }
};

cognito-auth.js

ログインIDとパスを入力するinput、ログインボタンをhtmlページ上に用意すること。(login.html参照)

// 認証トークン管理変数。全jsファイル間で共有
var tokenManager = window.tokenManager || {};

// 起動時処理
(function cognitoAuthoMain($) {

    // ユーザープールの取得

    var poolData = {
        UserPoolId: _config.cognito.userPoolId,
        ClientId: _config.cognito.userPoolClientId
    };

    var userPool;

    if (!(_config.cognito.userPoolId &&  _config.cognito.userPoolClientId && _config.cognito.region)) {
        return;
    }

    userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);

    if (typeof AWSCognito !== 'undefined') {
        AWSCognito.config.region = _config.cognito.region;
    }

    // ===

    // tokenManager に 関数を設定しておく

    // サインアウト処理
    tokenManager.signOut = function signOut() {
        userPool.getCurrentUser().signOut();
    };

    // 認証処理。「check-login-state.js」ファイルにて利用される。
    tokenManager.authToken = new Promise(function fetchCurrentAuthToken(resolve, reject) {
    
        var cognitoUser = userPool.getCurrentUser();

        if (cognitoUser) {
            cognitoUser.getSession(function sessionCallback(err, session) {
                if (err) {
                    reject(err);
                } else if (!session.isValid()) {
                    resolve(null);
                } else {
                    resolve(session.getIdToken().getJwtToken());
                }
            });
        } else {
            resolve(null);
        }
    });


    // ===

    // AWS Cognito 認証処理

    // ログイン処理
    function loginFunc(email, password, onSuccess, onFailure) {
        var authenticationDetails = new AmazonCognitoIdentity.AuthenticationDetails({
            Username: email,
            Password: password
        });

        var cognitoUser = createCognitoUser(email);
        cognitoUser.authenticateUser(authenticationDetails, {
            onSuccess: onSuccess,
            onFailure: onFailure
        });
    }

    function createCognitoUser(email) {
        return new AmazonCognitoIdentity.CognitoUser({
            Username: email,
            Pool: userPool
        });
    }

    // ===

    // ページ内処理・イベント

    // ページ読み込み時の処理
    $(function onDocReady() {

        // ログインボタンにイベント追加
        let aTag_login = document.getElementById("a_login");
        if( aTag_login ) {
            aTag_login.href = "javascript:void(0);";
            aTag_login.onclick = function() { handleLogin(); };
        }

        // ログアウトボタンにイベント追加
        let aTag_logout = document.getElementById("a_logout");
        if( aTag_logout ) {
            aTag_logout.href = "javascript:void(0);";
            aTag_logout.onclick = function() { handleLogout(); };
        }
    });

    // ログインボタン押下時
    function handleLogin() {

        // フォームに入力された値を取得
        var email = $('#login_input_email').val();
        var password = $('#login_input_password').val();
        
        // 入力チェック
        if( (email.length == 0) || (password.length == 0) ) {
            alert('入力されていない項目があります。ご確認ください。');
            return;
        }

        // ===

        // ログイン処理実行
        // 入力された値でログイン判定、成功したならページ遷移
        loginFunc( email, password,

            function loginSuccess() {

                console.log('Successfully Logged In');

                // ログイン成功でトップページへ遷移
                window.location.href = topPageUrl;
            },

            function loginError(err) {

                console.log('Failed Logged In');

                alert('ログインに失敗しました。IDとパスワードをご確認ください。');
            }
        );
    }

    // ログアウトボタン押下時
    function handleLogout() {

        // ログアウト処理
        tokenManager.signOut();

        // アラート表示
        alert('ログアウトしました。');

        // ログアウトでログインページへ遷移
        window.location.href = loginPageUrl;
    }

}(jQuery));

check-login-state.js

// 認証トークン管理変数。全jsファイル間で共有
var tokenManager = window.tokenManager || {};

// 以降のjsにてAPI認証に使われるトークン。
var authToken;

// 起動時処理
(function checkLoginStateMain($) {
    
    // 認証失敗時に戻すページのURL。今回はログイン画面に戻す想定。
    let loginPageUrl = 'login.html';
    
    // ===

    tokenManager.authToken.then(function setAuthToken(token) {

        if (token) {
            authToken = token;
        } else {
            alert("ページにアクセスするにはログインを行ってください。ログイン画面に移動します。");
            window.location.href = loginPageUrl;
        }

    }).catch(function handleTokenError(error) {
        
        alert(error);
        window.location.href = loginPageUrl;
    });

}(jQuery));

call-api.js

「check-login-state.js」にて取得したトークン「authToken」を、API呼び出し時に渡している。

// 認証トークン管理変数。全jsファイル間で共有
var tokenManager = window.tokenManager || {};

// 引数に、Lambdaに渡すJSONデータと、完了時の関数を渡す
// completeFuncは、引数にJSONデータを受け取るので、引数を1つ渡せるような関数とする 
function requestApi( jsonData, completeFunc ) {
    $.ajax({
        method: 'POST',
        
        url: _config.api.invokeUrl + '/admin',
        
        headers: {
            Authorization: authToken
        },

        data: jsonData,
        
        contentType: 'application/json',
        
        success: completeFunc,
        
        error: function ajaxError(jqXHR, textStatus, errorThrown) {
            console.error('Error requesting: ', textStatus, ', Details: ', errorThrown);
            console.error('Response: ', jqXHR.responseText);
            alert("データベースサーバーとの通信に失敗しました。");
        }
    });
}

main.js

call-api.js で準備したAPIコール関数を実行する。APIから応答を受けた後の処理も記述している。

(function MainFunction($) {

    // Lambda側に渡したいデータをJSON形式で準備
    var requestJsonData = {
                            "Key1": "Value1",
                            "Key2": "Value2"
                          };

    // Lambda実行完了時の処理
    function completeRequest( result ) {
        
        // 引数resultは受け取った時点でJSONデータとして認識できる
        // キーを指定してデータを得られる。複数データも配列として指定して取得すれば良い。
        
        // console.log('Response received from API: ', result);
        // const responseData = JSON.parse( result );
        
        console.log( result["ReceivedVal1"] );
        console.log( result["ReceivedVal2"] );
    }

    // ページ読み込み時の処理
    $(function onDocReady() {

        // Lambda実行
        requestApi( JSON.stringify(requestJsonData), completeRequest );
    });

}(jQuery));

Html

login.html

<!doctype html>
<html lang="ja">

<head>
<meta charset="utf-8">
<title>ログインページ サンプル</title>
</head>

<body>
<h1>ログインページ サンプル</h1>

<table>
<tr><th>ID:</th><td><input id="login_input_email" class="input_text" type="text" required /></td></tr>
<tr><th>パスワード:</th><td><input id="login_input_password" class="input_text" type="password" required /></td></tr>
</table>

<p id="btn_login"><a id="a_login">ログイン</a></p>

<!-- javascript -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>

<!-- Cognito認証 -->
<script src="html/js/vendor/aws-cognito-sdk.min.js"></script>
<script src="html/js/vendor/amazon-cognito-identity.min.js"></script>
<script src="html/js/config.js"></script>
<script src="html/js/cognito-auth.js"></script>

</body>
</html>

index.html

<!doctype html>
<html lang="ja">

<head>
<meta charset="utf-8">
<title>AWSチュートリアル簡易版テストページ</title>
</head>

<body>

<div>Hello World</div>

<!-- jquery -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.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>
<script src="js/check-login-state.js"></script>

<!-- API呼び出し処理 -->
<script src="js/config.js"></script>
<script src="js/call-api.js"></script>
<script src="js/main.js"></script>

</body>
</html>