はじめに
ユーザー情報管理の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」を記載すると、画面からログイン処理を実施できます。
ログイン処理のサンプル
ログイン画面のサンプルコードです。
<!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>
ユーザ新規作成処理のサンプル
ユーザ新規作成画面のサンプルコードです。
〜〜 ログイン処理のサンプルのコードに追加してください 〜〜
<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>
ユーザ新規作成処理のサンプル
ユーザ新規作成画面のサンプルコードです。
〜〜 ログイン処理のサンプルのコードに追加してください 〜〜
<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>
おわりに
読んで頂き、ありがとうございました。
個人的に調査した内容なので、少しでもお役に立てればと思います。
以上、よろしくお願いいたします。