以前の書籍を読んだ記事にも書きましたが、その書籍中では Cognito を使いましたが、ユーザープール (User Pools)については紹介だけで、チュートリアルはありませんでした。
blog.mori-soft.com
このユーザープールが気になり、簡単なサンプルを作って試してみましたので、メモにまとめておきます。
Cognito ユーザープールとフェデレーティッドアイデンティティの関係について
最初、ドキュメントを呼んでも、すっきりしなかったので、不正確かもしれませんが、自分なりに理解したユーザープールとフェデレーティッドアイデンティティの関係についてを書いておきます。
Cognito のドキュメントを読むと、ユーザープールは、独自で認証を実装するときの Users テーブルのようなものだとわかります(ディレクトリサービスといったほうがいいのかな)。
ユーザーの登録・管理・認証を提供してくれるサービスというところですかね。
もちろん、ユーザープール単独でも使えるのですが、Cognito フェデレーティッドアイデンティティ (Federated Identities) とつなげることで、
- ユーザープールで認証
- フェデレーティッドアイデンティティ で AWS アクセス用のロールを割り当て
- 割り当てられたロールを使って、AWSのサービスへのアクセスを行う
ということが可能になるようです。
で、わかりにくかったのが、このフェデレーティッドアイデンティティ との連携というところです。
最初の自分の理解だと、フェデレーティッドアイデンティティというと外部のIDプロバイダ(facebookやGoogle+)を利用するためのサービスだと、捉えていました。
でもこれって、外部のIDプロバイダだけじゃなくて、Cognito ユーザープールをIDプロバイダとして利用することができるようです。
そのあたりが分かると話がしっくりきます
(あらためてドキュメントを読んでみると、Cognito User Poolsも使えるよとしっかり書ていますね)。
また、フェデレーティッドアイデンティティを利用するためには、 Identity Pools (IDプールとも書かれてます)を作成する必要があります。
ここでも、『プール』、というキーワードが出てくるので、ややこしく感じますね。前述のユーザープールとは別物ですので、気を付けましょう。
このIDプールは、ユーザープールを含むIDプロバイダのアカウントに対して紐づけられる、Identity ID を管理するためのものです。この Identity ID に対してawsのロールを割り当てることになります。
こうすることで、IDプロバイダのアカウントを直接扱わなくても、認証したユーザーを一意に特定・管理できるということのようです。
ネットの記事によっては、ユーザープールの利用という話題について、フェデレーティッドアイデンティティを使う、という表現ではなく、Identithi Pool を使う、とのみ書いている場合があります。
どちらの表現でも、やってることは同じなので、わかってしまえば問題ないのですが、よくわからないうちだと、2種類の用語が出てくるけど、どう違うんだ?となってました。ご参考までに。
Cognito ユーザープール と Identity Pools の作成
上記の関係性さえわかれば、ユーザープルおよび Identity Pools の作成は、今回のようなサンプルだとたいして難しくありません。
ユーザープールの作成と Identity Pools の作成は、下記記事などを参考にしました。
Amazon Cognito User Poolsを使って、webサイトにユーザ認証基盤を作る - Qiita
Cognito User Pools x ログイン認証 x API認証 - Qiita
ユーザープール作成時の注意点としては、『アプリクライアント』を追加する際に、『クライアントシークレットを生成する』のチェックボックスを外すことを忘れないでください(JavaScriptからはクライアントシークレットが利用できないためです)。こちらのドキュメントもご参考に。
ブラウザでの動作サンプル
Identity Pool の作成まで問題なく終われば、コンソールでの作業は終わりです。
次は、ブラウザから、Cognito ユーザープールを利用するサンプルを作ります。
JavaScript からCognito の機能を使うためには、
- aws-cognito-sdk.js
- amazon-cognito-identity.js
の2つのSDKが必要です。下記のGitHubページからダウンロードできます。
github.com
とりあえずローカルにダウンロードして、それを指定します(このサンプルを試した時点では v1.19 でした)。
また、ログイン時のサンプルでは、AWS SDK for JavaScript も必要になります。
このGitHubのページにあるように、scriptタグで指定すればOKです。
ダウンロード版がよければ、こちらのページからデフォルトビルドのダウンロードなどを選択すれば、ダウンロードページに行けます。
サンプルコードの動かし方
なお、参考までに、以下で載せているサンプルをGitHubにあげておきましたので、もし興味があればご参考にしてください。
github.com
ローカルで動作させます。サーバーはpythonを利用しています。
(インターネット上のサーバー上が良ければ、S3などで適当にホスティングしてください。)
./run.sh
なお、動作環境は、
- Windows 10 Pro, 1703, 64bit
- Bash on Ubuntu on Windows, 16.04.2 LTS
です。
サインアップ
サインアップ用ページはこんな感じにしました。
コードはこんな感じですね(app.jsというファイルにまとめて書いてます)。
'use strict'
var upsample = {};
upsample.poolData = {
UserPoolId: 'ユーザープールのID',
ClientId: 'アプリクライアントのID'
};
upsample.UserPool = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserPool(upsample.poolData);
upsample.signup = function() {
var email = $('#inputEmail').val();
var username = $('#inputUserName').val();
var password = $('#inputPassword').val();
if (!email | !username | !password) { return false; }
var attributeEmail = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserAttribute({Name: 'email', Value: email});
var attributeList = [];
attributeList.push(attributeEmail);
var message_text;
upsample.UserPool.signUp(username, password, attributeList, null, function(err, result){
if (err) {
console.log(err);
message_text = err;
} else {
var cognitoUser = result.user;
console.log('user name is ' + cognitoUser.getUsername());
message_text = cognitoUser.getUsername() + ' が作成されました';
}
$('#message').text(message_text);
$('#message').show();
});
}
ユーザープールを設定した際に、デフォルト設定のままだと、e-mail のみが必須入力となっているので、ユーザー名、パスワード、メールアドレスを指定しています。
サインアップの確認
画面はこんな感じ。
コードはこちら。
upsample.verify = function() {
var username = $('#inputUserName').val();
var vericode = $('#inputVerificationCode').val();
if (!username | !vericode) { return false; }
var userData = {
Username: username,
Pool: upsample.UserPool
};
var message_text;
var cognitoUser = new AWSCognito.CognitoIdentityServiceProvider.CognitoUser(userData);
cognitoUser.confirmRegistration(vericode, true, function(err, result) {
if (err) {
console.log(err);
message_text = err;
$('#message').text(message_text);
$('#message').append($('<a href="resend.html">再送信</a>'));
} else {
console.log('call result ' + result);
message_text = cognitoUser.getUsername() + ' が確認されました';
$('#message').text(message_text);
}
$('#message').show();
});
}
サインアップページからサインアップの確認ページが表示されないのはご愛敬にしてください。
ログイン
ログインページはこんな感じです。
コードはこんな感じ。
upsample.login = function() {
var username = $('#inputUserName').val();
var password = $('#inputPassword').val();
if (!username | !password) { return false; }
var authenticationData = {
Username: username,
Password: password
};
var authenticationDetails = new AWSCognito.CognitoIdentityServiceProvider.AuthenticationDetails(authenticationData);
var userData = {
Username: username,
Pool: upsample.UserPool
};
var message_text;
var cognitoUser = new AWSCognito.CognitoIdentityServiceProvider.CognitoUser(userData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function(result) {
console.log('access token + ' + result.getAccessToken().getJwtToken());
AWS.config.region = 'ap-northeast-1';
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'Identity Pool の ID',
Logins: {
'cognito-idp.リージョン名.amazonaws.com/ユーザープールID': result.getIdToken().getJwtToken()
}
});
AWS.config.credentials.refresh(function(err) {
if (err) {
console.log(err);
} else {
console.log("success");
console.log("id:" + AWS.config.credentials.identityId);
}
$(location).attr('href', 'mypage.html');
});
},
onFailure: function(err) {
alert(err);
}
});
}
もし、ログイン後、AWSのサービスにアクセスするなど、Identity ID が必要になるならばここでidentityIDを取得することができます。
上記のサンプルだと、コンソールに出力してみて確認しているだけですが。
AWS のコンソールから確認すると、
のように、IDがちゃんと割り当てられているのが分かります。
マイページ
ログインに成功するとマイページが表示されます。
コードはこんな感じ。
現在のユーザーとセッションを確認して、セッションが有効であれば、ユーザー情報を表示しています。
upsample.checkSession = function () {
var cognitoUser = upsample.UserPool.getCurrentUser();
if (cognitoUser != null) {
cognitoUser.getSession(function (err, sessionResult) {
if (sessionResult) {
var attrs;
cognitoUser.getUserAttributes(function (err, attrs) {
if (err) {
console.log(err);
return;
}
$('#username').text('Username:' + cognitoUser.getUsername());
for (var i = 0; i < attrs.length; i++) {
console.log('name:' + attrs[i].getName() + ", value: " + attrs[i].getValue() );
if (attrs[i].getName() == 'email') {
$('#email').text('Email: ' + attrs[i].getValue());
}
}
});
} else {
console.log("session is invalid");
$(location).attr('href', 'login.html');
}
});
} else {
console.log("no user");
$(location).attr('href', 'login.html');
}
}
ログアウト
upsample.logout = function() {
var cognitoUser = upsample.UserPool.getCurrentUser();
if (cognitoUser != null) {
cognitoUser.signOut();
location.reload();
}
}
その他
実は、ログイン後、 Identity ID を取得するところでずいぶんはまりました。ようは、refreshメソッドを呼び出す必要があったんですが、それに気づかなかったのです。
ドキュメントのサンプルを見ると、ちゃんと書いてました。
きちんと読むべきですね。
あと、Identity ID (フェデレーティッドアイデンティティの Identity Pools の Identity ID)をコンソールで削除後、再度ブラウザからアクセスすると、『リソースがない』という旨のエラーで落ちることがありました。
調べてみると、 Cognito はlocalstorageを使っているようで、そのキャッシュが残っている関係で落ちるそうです。
stackoverflow.com
とりあえず、ブラウザの履歴を削除して、再度アクセスすると問題なく動作しました。
ただこの場合、Identity ID の値は、削除前の値から変更されています。なので、Identity ID を削除する必要があり、かつ、新旧のマッチを取る必要がある場合は、自分でなんとかしないといけなさそうですので、気を付けましょう。
参考
Cognito, Lambda, API Gateway のサンプル。Reactで作ってるので、React分かる人にはよいかも。
Cognito User Pools x ログイン認証 x API認証 - Qiita
Cognito利用時のログインの流れの図がわかりやすい
[ Serverless ] Cognito、S3、Lambdaで認証機能付きのWebサイトを作ってみました - Qiita
コード全般は下記記事を参考にしました。
AWS SDK for JavaScriptを使ってブラウザーからCognito User Poolsへサインアップしてみた | Developers.IO
AWS SDK for JavaScriptでCognito User Poolsを使ったログイン画面を作ってみた | Developers.IO