6
2

はじめに

ユーザー情報管理のAWSのランニングコストを下げるために、Amazon Cognitoについて調査した内容を記載します。

Cognitoとは

Cognitoは、ユーザー情報を管理するためのAWSサービスです。RDSなどでユーザー情報を管理するよりも遥かにランニングコストが低いですが、
ユーザー情報の一括取得が60件までしか取得できないことが解りました。61件以上の取得を試みるとエラーが発生します(一括取得には適切なロール権限が必要です)。そのため、ユーザー一覧画面の作成には不向きなサービスと言えます。

ユーザーの最大登録数を60件までに制限するなどのルールを設ければ一覧画面は作成できます。しかし、ユーザー情報を変更する際には、変更対象のユーザーの「パスワード」と「ユーザー名」が必要だったり、パスワードの再発行には、登録されている「Eメール」または「電話番号」を使用して「確認コード」を発行し、その確認コードを用いてパスワードの変更処理を行う必要があります。このため、Cognitoは管理者が自由にユーザー情報を変更することができず、柔軟性に欠ける部分があります。
しかし、Cognitoはモバイルアプリやウェブアプリのユーザー管理には非常に向いています。これにより、ユーザー登録、ログイン、パスワードリセット、多要素認証(MFA)などの機能を簡単に実装することができます。

Cognito機能のまとめ

  • 60件までのユーザー情報なら一括で取得できます。ただし、適切なロール権限が必要です。

  • パスワードを忘れてしまった場合、再発行が可能です。ただし、「Eメール」または「電話番号」による確認コードの発行が必要です。パスワード再発行時には、確認コード、ユーザー名、新しいパスワードの入力が必要です。

  • ユーザー名は変更できませんが、ユーザー名以外の設定項目は変更可能です。ただし、変更時には「ユーザー名」と「パスワード」が必要になります。

  • ユーザー情報を新規作成する際には、「Eメール」または「電話番号」を使用して「確認コード」を発行し、ステータスを「未確認」から「確認済み」に変更する必要があります。AWSマネジメントコンソールのCognito画面からもステータスの変更が可能です。

  • AWSマネジメントコンソールのCognito画面からユーザー情報を新規追加した場合、管理者が追加したユーザーと判断されるため、初回ログイン時にパスワード変更処理が必要になります(初回のみ変更が必要です)。

Cognitoの作成手順

AWSマネジメントコンソールのCognito画面にて「ユーザープール」を作成して「ユーザープールID」と「クライアントID」を発行します。
IDプールは、ユーザープールと連携させて、ユーザープールに登録されているユーザーに対してAWSサービスへのアクセス権限を設定するためのものです。(アクセス権限を設定する場合は、IDプールも作成します)

下記のサンプルコードに作成した「ユーザープールID」と「クライアントID」を記載すると、画面からログイン処理を実施できます。

ログイン処理のサンプル

ログイン画面のサンプルコードです。

index.html
<!DOCTYPE html>
<html llang= "ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ログイン</title>
    <script src="https://cdn.jsdelivr.net/npm/amazon-cognito-identity-js@latest/dist/amazon-cognito-identity.min.js"></script>
    <script src="https://sdk.amazonaws.com/js/aws-sdk-2.814.0.min.js"></script>
    <script>

        const poolData = {
            UserPoolId: 'ap-****-*_*********', // ここを作成したユーザープールIDに置き換える
            ClientId: '8p*******************', // ここを作成したクライアントIDに置き換える
        };

        const userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);

        // ログイン処理
        function logIn(event) {
            event.preventDefault();
            const username = document.getElementById("username").value;
            const password = document.getElementById("password").value;
            let text1 = document.getElementById('text1');

            const authenticationData = {
                Username: username,
                Password: password
            };
            const authenticationDetails = new AmazonCognitoIdentity.AuthenticationDetails(authenticationData);

            const userData = {
                Username: username,
                Pool: userPool
            };
            const cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData);

            cognitoUser.authenticateUser(authenticationDetails, {
                onSuccess: function(result) {
                    text1.innerHTML = 'ログイン成功 : ' + cognitoUser.getUsername() + '<br>';
                },
                onFailure: function(err) {
                    if (err.code === 'UserNotConfirmedException') {
                        text1.innerHTML = 'ユーザー認証されていません。<br>';
                        resendConfirmationCode(username, text1);
                    }else{
                        text1.innerHTML = 'ログイン失敗しました : ' + (err.message || JSON.stringify(err)) + '<br>';
                    }
                },
                newPasswordRequired: function(userAttributes, requiredAttributes) {
                    const newPassword = prompt('新しいパスワードを入力してください');
                    cognitoUser.completeNewPasswordChallenge(newPassword, {}, {
                        onSuccess: function(result) {
                            text1.innerHTML = 'パスワード変更しました : ' + cognitoUser.getUsername() + '<br>';
                        },
                        onFailure: function(err) {
                            text1.innerHTML = 'ログインエラー : ' + (err.message || JSON.stringify(err)) + '<br>';
                        }
                    });
                }
            });
        }

        // 認証メール送信処理
        function verify(event) {
            // デフォルトのフォーム送信を無効にする
            event.preventDefault();
            var username = document.getElementById("new_username").value;
            var verificationCode = document.getElementById("verification-code").value;
            let text1 = document.getElementById('text1');

            var userData = {
                Username: username,
                Pool: userPool
            };
            var cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData);
            cognitoUser.confirmRegistration(verificationCode, true, function(err, result) {
                if (err) {
                    text1.innerHTML = '認証に失敗しました : ' + (err.message || JSON.stringify(err)) + '<br>';
                } else {
                    text1.innerHTML = '認証に完了しました : ' + result + '<br>';
                }
            });
        }

        // 再認証メール送信処理
        function resendConfirmationCode(username, text1) {
            var userData = {
                Username: username,
                Pool: userPool
            };
            var cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData);
            document.getElementById('verification').style.display = 'block';
            cognitoUser.resendConfirmationCode(function(err, result) {
                if (err) {
                    text1.innerHTML = '再認証に失敗しました : ' + (err.message || JSON.stringify(err)) + '<br>';
                    return;
                }else{
                    text1.innerHTML = '登録されているメールに確認コードを再送信しました.<br>';
                }
            });
        }
    </script>
    <style>
        .block {
            margin: 20px;
            padding: 20px 20px 40px 20px;
            width: 500px;
            background-color: #ffffff;
            border: 1px solid #ccc;
            border-radius: 5px;
        }
        .block1 {
            margin: 20px;
        }
        .block2 {
            padding: 10px;
            margin-top: 5px;
            border: 1px solid #212121;
            border-radius: 5px;
            width: 520px;
            background-color: #fffbf2;
        }
        h2 {
            text-align: center;
            margin-bottom: 20px;
        }

        input[type="text"],
        input[type="password"],
        input[type="email"]{
            width: 96%;
            padding: 10px;
            margin-bottom: 10px;
            border: 1px solid #ccc;
            border-radius: 4px;
        }
        button[type="submit"] {
            width: 100%;
            margin-top: 10px;
            padding: 10px;
            background-color: #4CAF50;
            color: #ffffff;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
        button[type="submit"]:hover {
            background-color: #45a049;
        }
    </style>
</head>
<body>

    <div class="block">
        <form id="login-form">
            <p class="fsize">ログイン画面</p>
            <label for="username">ユーザ名</label>
            <input type="text" name="username" id="username" placeholder="ユーザ名">
            <label for="password">パスワード</label>
            <input type="password" name="password" id="password" placeholder="パスワード">
            <button type="submit" onclick="logIn(event);">ログインする</button>
        </form>
    </div>
    
    <div class="block" id="verification" style="display:none;">
        <form id="verification-form">
            <p class="fsize">確認コードを入力してください</p>
            <label for="verification-code">確認コード</label>
            <input type="text" name="verification-code" id="verification-code" placeholder="確認コード" required>
            <button type="submit" onclick="verify(event);">確認する</button>
        </form>
    </div>

    <div class="block1">
        <p style="margin-top: 30px;">【ログ出力】</p>
        <div id="text1" class="block2">
        </div>
    </div>
</body>
</html>

ユーザ新規作成処理のサンプル

ユーザ新規作成画面のサンプルコードです。

index.html

〜〜 ログイン処理のサンプルのコードに追加してください 〜〜

<script>
// ログイン新規作成処理
function signUp(event)  {
    event.preventDefault();
    const username = document.getElementById("new_username").value;
    const password = document.getElementById("new_password").value;
    var email = document.getElementById("email").value;
    let text1 = document.getElementById('text1');

    var attributeList = [];
    var dataEmail = {
        Name: 'email',
        Value: email
    };
    var attributeEmail = new AmazonCognitoIdentity.CognitoUserAttribute(dataEmail);
    attributeList.push(attributeEmail);

    try {
        userPool.signUp(username, password, attributeList, null, function(err, result) {
            if (err) {
                text1.innerHTML = 'ユーザ作成失敗 : ' + (err.message || JSON.stringify(err)) + '<br>';
            } else {
                var cognitoUser = result.user;
                text1.innerHTML = '新規作成しました : ' + cognitoUser.getUsername() + '<br>';
                text1.innerHTML += '確認コードをメールで送信しました。コードを入力してください。<br>';
                document.getElementById('verification').style.display = 'block';
            }
        });
    } catch (error) {
        text1.innerHTML = 'ユーザ作成失敗 : ' + error.message + '<br>';
    }
}
</script>

<body>
    <div class="block">
        <form id="signup-form">
            <p class="fsize">ユーザ登録画面</p>
            <label for="new_username">ユーザ名</label>
            <input type="text" name="new_username" id="new_username" placeholder="ユーザ名">
            <label for="new_password">パスワード</label>
            <input type="password" name="new_password" id="new_password" placeholder="パスワード">
            <label for="email">認証を確認するメールアドレス</label>
            <input type="email" name="email" id="email" placeholder="メールアドレス">
            <button type="submit" onclick="signUp(event);">新規登録する</button>
        </form>
    </div>
</body>

ユーザ新規作成処理のサンプル

ユーザ新規作成画面のサンプルコードです。

index.html

〜〜 ログイン処理のサンプルのコードに追加してください 〜〜

<script>
async function changePassword(event) {
    event.preventDefault();
    const username = document.getElementById('change-username').value;
    const oldPassword = document.getElementById('old-password').value;
    const newPassword = document.getElementById('new-password').value;
    const text1 = document.getElementById('text1');
    const userData = {
        Username: username,
        Pool: userPool
    };
    const cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData);
    const authenticationData = {
        Username: username,
        Password: oldPassword
    };
    const authenticationDetails = new AmazonCognitoIdentity.AuthenticationDetails(authenticationData);
    cognitoUser.authenticateUser(authenticationDetails, {
        onSuccess: function(result) {
            cognitoUser.changePassword(oldPassword, newPassword, function(err, result) {
                if (err) {
                    text1.innerHTML = 'パスワード変更エラー : ' + (err.message || JSON.stringify(err)) + '<br>';
                } else {
                    text1.innerHTML = 'パスワードの変更に成功しました。<br>';
                }
            });
        },
        onFailure: function(err) {
             text1.innerHTML = 'パスワード変更エラー : ' + (err.message || JSON.stringify(err)) + '<br>';
        }
    });
}
</script>

<body>
    <div class="block">
        <form id="change-form" onsubmit="changePassword(event);">
            <p class="fsize">パスワード変更画面</p>
            <label for="change-username">ユーザー名:</label>
            <input type="text" id="change-username" name="change-username" required>
            <label for="old-password">現在のパスワード:</label>
            <input type="password" id="old-password" name="old-password" required>
            <label for="new-password">新しいパスワード:</label>
            <input type="password" id="new-password" name="new-password" required>
            <button type="submit" onclick="changePassword(event);">パスワード変更する</button>
        </form>
    </div>
</body>

おわりに

読んで頂き、ありがとうございました。
個人的に調査した内容なので、少しでもお役に立てればと思います。
以上、よろしくお願いいたします。

6
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2