Posted at

モダンなフロントエンド開発スタイルを知らずにAmazon Cognitoを試してみた


AWSマネージドサービスを試したい

やっぱり認証が使えると捗る気がする、ということで、Amazon Cognitoを試してみました。

Amplifyというライブラリ群がリリースされて実装が簡易になったという話は聞いた…だけ。


コンソールから設定

まずどこからとっつけばいいのかというところですが、AWSコンソールからCognitoを触ってみましょう。

色んなサイトを参考にさせていただきました。

ポイントとしては、


  1. ユーザープールの作成


    • 「クライアントシークレットを生成」のチェックを外す



  2. IDプールの作成


    • 認証プロバイダを「Cognito」とし、上でつくったユーザープールID・アプリクライアントIDをセット



となります。

今回はログインIDをメールアドレスとする設定にし、ユーザー自身が登録(サインアップ)できるようにしました。

また、MFA認証(二段階認証)はOFFにしています。

コンソールのユーザープールからユーザー追加ができたりします。

試しに一件仮パスワード発行状態でユーザー作成しました。

すると仮パスワードの通知メールが届きます。


サンプルアプリ作成


準備

さて、問題はここから。

以前は複数のSDKを入れなければならないとかあったようですが、現状ではAmplifyがベストプラクティスなのではないか?と。

Amplify Framework

とはいえ、ReactやVueでさくっと試せるスキルがないので、JavaScriptでのサンプルを試してみようと思ったところ…。

どうもAmplifyは上でコンソールでやったようなユーザープールの作成からCLIベースで対話式でするものらしい。

あれ…? コンソールからつくっちゃたよ…。どうする?

色々と調べてみると、やはり設定する方法はある模様。

Amplify JavaScript - Authentication#Manual Setup

よし、とりあえずAmplifyをインストールじゃ。

たまたまローカルのWindows10環境にnodistをよくわからないまま入れていたので、npmは使えそう。

【参考】

nodistでNode.jsをバージョン管理

適当なフォルダを掘って、Getting Startedにしたがってpackage.jsonをコピペしてnpm install

おお、node_modulesの下になんかたくさん落ちてきたぞ~。

そして手順通りにwebpack.config.jsもコピペしてnpm start

おお、いろいろコンパイルされてローカルサーバーが立ち上がった~。

localhost:8080でサンプルページにアクセスできます。

え…XAMPPとか、VirtualBoxとVagrantとか要らんの…?

npm(Node.js?)すごくね…? ←そこかよ


サンプル実装

さて、無事環境が整ったので、ここから本題、Cognitoで遊んでみます。

上記Getting Startedは以降、amplifyコマンドを使った環境構築に進みそうなので、一旦離脱。

Amplify JavaScript - Authenticationを参考にそれぞれの処理を実装していきます。

※このあと、webpackでビルドしたfunctionの扱いにハマったりしたのですが、脇道なのでさらっと…

【参考】

webpackでビルドしたbundle.jsの関数をonclickで動かしたかった話。


①config

Manual Setupにしたがって記述します。

必要最小限?のIDのみ。

importのところだけサンプルの書き方だとうまく行かなかったので書き換えました。


app.js

import Amplify from '@aws-amplify/core';

import Auth from '@aws-amplify/auth';

Amplify.configure({
Auth: {
// REQUIRED only for Federated Authentication - Amazon Cognito Identity Pool ID
identityPoolId: 'ap-northeast-1:xxxxxxxx...',

// REQUIRED - Amazon Cognito Region
region: 'ap-northeast-1', //東京リージョン

// OPTIONAL - Amazon Cognito User Pool ID
userPoolId: 'ap-northeast-1_xxxx...',

// OPTIONAL - Amazon Cognito Web Client ID (26-char alphanumeric string)
userPoolWebClientId: 'xxxxxxxx...',
}
});

const currentConfig = Auth.configure();



②ログイン(サインイン)

先にコンソールでテストユーザーを登録していたので、ログインできるか試してみましょう。

Sign Inにしたがって、画面(index.html)やプロンプトからユーザー入力値を受け取れるようにしてみます。


app.js

let SignIn = async function () {

try {
const username = document.getElementById("userid").value;
const password = document.getElementById("passwd").value;
const user = await Auth.signIn(username, password);
console.log(user);

if (user.challengeName === 'SMS_MFA' ||
user.challengeName === 'SOFTWARE_TOKEN_MFA') {

} else if (user.challengeName === 'NEW_PASSWORD_REQUIRED') {
const {requiredAttributes} = user.challengeParam; // the array of required attributes, e.g ['email', 'phone_number']
// You need to get the new password and required attributes from the UI inputs
// and then trigger the following function with a button click
// For example, the email and phone_number are required attributes
const newPassword = prompt("新しいパスワードを入力してください。");
if (!newPassword) {
alert("新しいパスワードは必須です。");
return;
}
const loggedUser = await Auth.completeNewPassword(
user, // the Cognito User Object
newPassword, // the new password
// OPTIONAL, the required attributes
{
// email: '',
// phone_number: '',
}
);
} else if (user.challengeName === 'MFA_SETUP') {

}

document.getElementById("AuthResult").innerHTML = user.attributes.name + ' としてログイン中';
document.getElementById("SignIn").style.display = "none";
document.getElementById("SignOut").style.display = "inline-block";
} catch (err) {
console.log(err);
document.getElementById("AuthResult").innerHTML = 'ログイン失敗:' + err.code;
}
}
window.SignIn = SignIn;


ログインボタンからSignInをコールすると…

おお、ログインできましたね。そして仮パスワードだったため、新しいパスワードが無事求められ、新しいパスワードが登録できました。

存在しないユーザーやパスワード間違いでそれぞれExceptionが返ってきていますし1、新しいパスワードで無事ログインできました。


③ログアウト(サインアウト)

次にログアウトしてみます。

Sign Outのサンプルほぼそのままです。


app.js

let SignOut = function () {

Auth.signOut()
.then(function(data) {
console.log(data);

document.getElementById("AuthResult").innerHTML = 'ログアウトしました';
document.getElementById("SignOut").style.display = "none";
document.getElementById("SignIn").style.display = "inline-block";
})
.catch(err => console.log(err));
}
window.SignOut = SignOut;


ログインした状態でログアウトボタンからSignOutをコールすると、無事ログアウトされました。


④ユーザー登録(サインアップ)

では、画面からユーザー登録してみましょう。

Sign Upにしたがい、登録→認証コード入力という流れを簡易的に実装します。


app.js

let SignUp = function () {

const username = document.getElementById("userid").value;
const password = document.getElementById("passwd").value;
const name = document.getElementById("username").value;

Auth.signUp({
username,
password,
attributes: {
name: name,
// email, // optional
// phone_number, // optional - E.164 number convention
// other custom attributes
},
// validationData: [] //optional
})
.then(function(data) {
console.log(data);
document.getElementById("AuthResult").innerHTML = 'アカウント仮登録:' + data.user.username;

confirmSignUp(data.user.username);
})
.catch(function(err) {
console.log(err);
document.getElementById("AuthResult").innerHTML = '登録失敗:' + err.code;
});
}
window.SignUp = SignUp;

function confirmSignUp(username) {
// After retrieving the confirmation code from the user
const code = prompt("認証コードを入力してください。");
Auth.confirmSignUp(username, code, {
// Optional. Force user confirmation irrespective of existing alias. By default set to True.
// forceAliasCreation: true
})
.then(function(data) {
console.log(data);
document.getElementById("AuthResult").innerHTML = 'アカウント登録完了';
})
.catch(err => console.log(err));
}


ユーザー登録ボタンからSignUpをコールすると、入力したアドレスに認証コードが届き、それをプロンプトに入力するとユーザー登録が完了しました。

AWSコンソールのユーザープールを見てみると、ユーザーが増えたことが確認できます。

そして新しいユーザーでもログインが問題なくできます。


⑤ログイン中かどうか

あとは今ログインしているかどうかです。

Retrieve Current Authenticated Userのとおりにやってみます。


app.js

let isLogIn = function () {

Auth.currentAuthenticatedUser({
// bypassCache: false // Optional, By default is false. If set to true, this call will send a request to Cognito to get the latest user data
}).then(function(user) {
console.log(user);

document.getElementById("AuthResult").innerHTML = 'あなたは' + user.attributes.name;
document.getElementById("SignIn").style.display = "none";
document.getElementById("SignOut").style.display = "inline-block";
})
.catch(err => console.log(err));
}
window.isLogIn = isLogIn;


画面読み込み時にisLogInをコールするようにすると、ログイン中であれば無事ログイン中である旨が表示されました。

あるユーザーでログインしたまま、別のユーザーでログインしましたが、返ってくるのは後者のユーザーのようです。


実験後

なかなかおもしろかったです。

アプリつくる度に認証実装するのもバカバカしいですし、そこだけ切り出してマネージドサービスでやってくれるのは嬉しいですね。

あとは実際やるとしたら、config情報をクライアント側で見せないようにするにはどうするの?あたりが気になるところですね。





  1. セキュリティ的には、ユーザーが存在するかどうかを返すのは微妙ですけどね…。Cognitoを使うとログインID(メールアドレス等)が存在するかどうか判別可能になる